// Переменные голосовух
let mediaRecorder;
let setIntervalSound;
let timerSecMessage = 0;
let flagVoice = false;
const chunksVoice = [];

Element.prototype.remove = function () {
    this.parentElement.removeChild(this);
}
NodeList.prototype.remove = HTMLCollection.prototype.remove = function () {
    let i = 0, len = this.length;
    for (; i < len; i++) {
        if (this[i] && this[i].parentElement) {
            this[i].parentElement.removeChild(this[i]);
        }
    }
}

const chat = {
    key: '',
    room: '',
    count: 0,
    time: 0,
    t: null, //timer
    t2: null, //timer 2
    t_all: {}, //time molch
    r: 0,
    g: 0,
    rtime: 37,
    ct: {'-1': 0, '1': 15, '2': 30, '3': 60, '4': 300},
    saveData: null,
    msg_id: 0,
    nrg: 0,
    nozpros: 0,
    newmsg: 0,
    globalMsg: 0,
    ignoreList: {x: 0, nms: []},
    inObj: null,
    userSendMessage: false,

    efftxt: function (id, txt) {
        return txt;
    },

    ignore: function (login) {
        if (this.ignoreList[login] !== undefined) {
            if ($('#ignr_alu').attr('id') !== undefined) {
                $(`#ignr_u_${this.ignoreList[login]}`).remove();
            }
            delete this.ignoreList.nms[this.ignoreList[login]];
            delete this.ignoreList[login];
            return
            //msg
        }

        this.ignoreList.x++;
        this.ignoreList[login] = this.ignoreList.x;
        this.ignoreList.nms[this.ignoreList.x] = login;

        let ingr_alu = $('#ignr_alu')

        if (ingr_alu.attr('id') !== undefined) {
            ingr_alu.html(`${ingr_alu.html()}<div id="ignr_u_${this.ignoreList.x}"><b>${login}</b> <a target="_blank" href="/info/${login}"><img alt="Инф. о ${login}" src="${c.img2}/i/inf_capitalcity.gif" onMouseOver="top.hi(this,\'<b>Инф. о ${login}</b>\',event,5,-2,2,2,\'\');" onMouseOut="top.hic();" onMouseDown="top.hic();"></a> <small><a href="javascript:void(0)" onclick="chat.ignorUn(${this.ignoreList.x})">Clear</a></small></div>`);
        }
    },

    getRandom: function (a, b) {
        return a + ((b - a) * Math.random());
    },

    feerverk_id: 0,

    feerverk: function (name) {
        const frc = {
            'name': 'fw04',
            'x': 19,
            'top': this.getRandom(1, 35),
            'left': this.getRandom(0, 365),
            'width': 135,
            'sound': this.getRandom(8, 10),
            'height': 99,
            'zad': 3
        };
        if (name === 'fw04') {
            frc.left -= 35;
        }

        if (frc.name) {
            const obj = top.frames.main.document.getElementById('frvrks');
            if (obj !== undefined) {
                let newhtml = '';
                let i = 1;
                while (i <= frc.x) {
                    newhtml += `<img alt="" style="display:none" id="frvanim_${this.feerverk_id}_img${i}" width="${frc.width}" height="${frc.height}" src="${c.img2}/fw/${frc.name}/${i}.gif">`;
                    i++;
                }
                newhtml = `<div id="frvanim_${this.feerverk_id}" style="z-index:5000;position:absolute;width:${frc.width}px;height:${frc.height}px;left:${frc.left}px;top:${frc.top}px;">${newhtml}</div>`;
                $(obj).append(newhtml);
                this.feerverk_go(this.feerverk_id, frc.x - 1, frc.name, frc.x, frc.zad);
                this.feerverk_id++;
            }
        }
    },
    feerverk_go: function (id, time_back, img, x, zad) {
        if (zad > 0) {
            setTimeout(`chat.feerverk_go(${id}, ${time_back}, "${img}", ${x}, 0);`, 500 * zad);
        } else {

            time_back--;
            top.frames.main.document.getElementById(`frvanim_${id}`);
            if (time_back > 0) {
                const img1 = top.frames.main.document.getElementById(`frvanim_${id}_img${(x - time_back + 1)}`); //текущая
                if (img1 !== undefined) img1.style.display = 'none';
                const img2 = top.frames.main.document.getElementById(`frvanim_${id}_img${(x - time_back + 2)}`); //текущая
                if (img2 !== undefined) img2.style.display = '';
                setTimeout(`chat.feerverk_go(${id}, ${time_back}, "${img}", ${x}, 0);`, 50);
            } else top.frames.main.document.getElementById(`frvanim_${id}`).remove();
        }
    },
    ignorUn: function (x) {
        $(`#ignr_u_${x}`).remove();
        this.ignore(this.ignoreList.nms[x]);
    },
    ignorListOpen: function () {
        let date = '';
        let j = 1;
        while (j <= this.ignoreList.x) {
            if (this.ignoreList[this.ignoreList.nms[j]] !== undefined) {
                date += `<div id="ignr_u_${j}">    <b>${this.ignoreList.nms[j]}</b> <a target="_blank" href="/info/${this.ignoreList.nms[j]}"><img alt="Инф. о ${this.ignoreList.nms[j]}" src="${c.img2}/i/inf_capitalcity.gif" onMouseOver="top.hi(this,\'<b>Инф. о ${this.ignoreList.nms[j]}</b>\',event,5,-2,2,2,\'\');" onMouseOut="top.hic();" onMouseDown="top.hic();"></a> <small><a href="javascript:void(0)" onclick="chat.ignorUn(${j})">Clear</a></small></div>`;
            }
            j++;
        }
        win.add('ignorListWin', 'Список игнорируемых', `<div id="ignr_alu">${date}</div>`, {}, 0, 1, 'min-width:200px;');
    },

    // смайлы
    addSmile: function (id) {
        let textmsg = $('#textmsg')
        textmsg.val(`${textmsg.val()} :${id}: `);
        textmsg.focus();
    },

    lookSmiles: function () {
        if ($('#chbtn8').attr('class') === 'db cp chatBtn8_1') {
            return this.lookSmilesWork('', 'chatBtn8_2')
        }
        return this.lookSmilesWork('none', 'chatBtn8_1')
    },
    lookSmilesWork: function (display, className) {
        $('#ttSmiles').css('display', display);
        $('#chbtn8').attr('class', `db cp ${className}`);
    },

    // оптимизация всех кнопок
    greyButton_work: function (elemId, className, method, count) {
        $(elemId).attr('class', `db cp ${className}`);
        this[method] = count;
    },

    subValSend: '',
    subSend: async function () {
        let textmsg = $('#textmsg')
        if (textmsg.val() !== '') {
            if (textmsg.val() !== this.subValSend) {
                if (this.nozpros === 0) {
                    const textmsg = $('#textmsg').val();
                    if (cb_date[cb_select] === 7 && (textmsg.match(/to *\[(.*?)].*/i) || textmsg.match(/private *\[(.*?)].*/i))) {
                        for (const cb in cb_date) {
                            if (cb_date[cb] === 5) {
                                open_cb(cb, null);
                            }
                        }
                    }
                    if (typeof window.online_send_jqxhr === "undefined" || window.online_jqxhr.readyState === 4) {
                        window.online_send_jqxhr = $.post('online.php?r' + c.rnd + '&cas' + ((new Date().getTime()) + Math.random()), {
                            msg: textmsg,
                            key: this.key,
                            mid: this.msg_id,
                            rndo: c.rnd,
                            cb: cb_date[cb_select]
                        }, function (data) {
                            chat.clearText();
                            chat.fc();
                            chat.genchatData(data, 1);
                            chat.scrollNow(true)
                            document.getElementById("textmsg").value = ''
                        });
                    }
                }
            }
        }
    },
    addto: function (login, type2) {
        let textmsg = $('#textmsg')
        let inObjId = $('#' + $(this.inObj).attr('id'))
        const loginaddT = login;
        let s = '';
        if (inObjId.attr('id') === undefined) {
            textmsg.focus();
            s = textmsg.val();
        } else $(this.inObj).focus();
        const reg555 = new RegExp("private\\s*\\[(.*?)\\]", "");
        const reg551 = new RegExp("to\\s*\\[(.*?)\\]", "");
        const test1 = s.match(reg555);
        let type;
        if (s.match(reg555) == null) {
            type = "to";
        } else if (s.match(reg551) == null) {
            type = "private";
        }
        let type3 = 'to';
        const reg2 = new RegExp("" + type + "(\\s*)\\[(.*?)\\]", "");
        const cs = s.replace(reg2, "" + type + "$1[,$2,]");
        const slogin = login.replace(/([\^.*{}$%?\[\]+|\/\(\)])/g, "\\$1");
        const reg = new RegExp("" + type + "\\s*\\[.*,\\s*" + slogin + "\\s*,.*\\]", "");
        let result = '';
        const reg3 = new RegExp("" + type + "\\s*\\[(.*?)\\]", "");
        let res;
        while (res = s.match(reg3)) {
            result += `${res[1]},`;
            s = s.replace(reg3, '');
        }
        result = result.replace(/,$/, '');
        const prar = result.split(',');
        for (let i = 0; i < prar.length; i++) {
            prar[i] = prar[i].replace(/^\s+/, '');
            prar[i] = prar[i].replace(/\s+$/, '');
        }
        const str = prar.join(', ');
        if (str) login += ', ';
        let space = '';
        if (!s.match(/^\s+/)) space = ' ';
        const prob = '';
        let main = $('#main')
        if (inObjId.attr('id') === undefined && (this.inObj == null || main.contents().find('#' + this.inObj.id).attr('id') === undefined)) {
            if (!cs.match(reg)) {
                if (type2 === 'to') {
                    if (test1 != null) type2 = 'private';
                }
                if (loginaddT === 'klan' && type2 === 'private') {
                    if (login === 'klan, ') {
                        s = type2 + ' [klan] to [' + prob + '' + str + '' + prob + ']' + space + s;
                    } else {
                        s = type2 + ' [klan]' + space + s;
                    }
                } else {
                    s = type2 + ' [' + prob + '' + login + str + '' + prob + ']' + space + s;
                }
            } else {
                if (type3 === 'to') type3 = "private";
                s = type3 + ' [' + prob + '' + str + '' + prob + ']' + space + s;
            }
        } else s = login + str;

        if (this.inObj != null && main.contents().find(`#${this.inObj.id}`).attr('id') !== undefined) {
            $('#main').contents().find(`#${this.inObj.id}`).val(s);
        } else if ($('#' + $(this.inObj).attr('id')).attr('id') === undefined) {
            $('#textmsg').val(s);
        } else $(this.inObj).val(s);
    },
    clearText: function () {
        $('#textmsg').val('');
    },

    reflesh: function () {
        this.time = 0;
        this.testTimer(true);
    },

    reMoney: function () {
        $('#moneyGM').html(`${c.money} кр.`);
    },
    testTimer: function (n) {
        clearTimeout(this.t);
        if ($.cookie('btl') !== this.btl) {
            if ($.cookie('btl') > 0) {
                if (!top.frames['main'].smnpty) {
                    top.frames['main'].location.href = "main.php";
                }
            }
            this.btl = $.cookie('btl');
        }
        if (this.rtime !== this.ct[$.cookie('chatCfg0')] && this.ct[$.cookie('chatCfg0')]) {
            this.rtime = this.ct[$.cookie('chatCfg0')];
            if (this.time > this.rtime) this.time = this.rtime;
        }
        if (this.rtime >= 5 || this.r === 0) //was >- 30 lakris fix
        {
            if (this.time < 1) {
                const aot = {
                    0: 0,
                    1: 1,
                    2: 0
                };
                if ($('#chcf10').attr('checked') === true) aot[2] = 1;
                if ($('#autoRefOnline').attr('checked') === true || this.r === 0 || n !== false) {
                    aot[0] = 1;
                }
                if (this.nozpros === 0 && (typeof window.online_jqxhr === "undefined" || window.online_jqxhr.readyState === 4)) {
                    window.online_jqxhr = $.getJSON(`online.php?r${c.rnd}&cas${((new Date().getTime()) + Math.random())}`, {
                        key: this.key,
                        mid: this.msg_id,
                        r1: aot[0],
                        r2: aot[1],
                        r3: aot[2],
                        rndo: c.rnd
                    }, function (data) {
                        if (data.rnd != null) {
                            chat.genchatData(data);
                            if (data.key !== undefined) chat.saveData = data;
                            this.g++;
                            c.rnd = data.rnd;
                        }
                    });
                }
                this.time = 5;
                this.r++;
            } else this.time--;
            this.t = setTimeout('chat.testTimer(false);clearTimeout(this.t);', 1000);
        }
    },

    gUser: function (data, ol) {
        let rt = '';
        // ["27205347","Merlin","8","1.99","Radminion","capitalcity","capitalcity","349","","","0","-4","0","0","","0",""]
        function data_work(count) {
            data[count] = data[count].replaceAll("\\", '\\\\');
            data[count] = data[count].replaceAll("[s1;]", '"');
            data[count] = data[count].replaceAll("[s2;]", '`');
            data[count] = data[count].replaceAll("[s3;]", '');
            data[count] = data[count].replaceAll("[s4;]", '');
            data[count] = data[count].replaceAll("<", '');
            data[count] = data[count].replaceAll(">", '');
        }

        if (data[1] === undefined) {
            return '<i>невидимка</i>[??]'
        }
        rt = data[1];
        if (rt === 'Администратор') rt = this.efftxt('fire', rt);
        if (ol === true) {
            rt = `<a href="javascript:void(0)" onClick="chat.addto(\'${data[1]}\',\'to\')">${rt}</a>`;
        } else rt = `<b>${rt}</b>`;
        if (parseInt(data[13]) !== 0) rt = `<span class="uCss${data[13]}">${rt}</span>`;

        if (parseInt(data[10]) > 0) {
            rt = `<s onmouseout="top.hic()" onmousedown="top.hic()" onmouseover="top.hi(this,\'Персонаж был заблокирован\',event,3,1,1,2,\'\')">${rt}</s>`;
        }

        if (data[8] !== undefined) data_work(8)
        else if (data[9] !== undefined) data_work(9)

        // тут
        function rtData(count) {
            return rt = `<img alt="Склонность" height="15" src="${c.img2}/i/align/align${data[count]}.gif">${rt}`;
        }

        if (parseInt(data[4]) !== 0 && clanImage !== undefined) {
            //data:${c.clanImage.mime_type};base64,${c.clanImage.img}
            rt = `<a href="/clan/${data[4]}" title="${data[4]}" target="_blank"><img alt="Клан" width="24" height="15" src="${clanImage}"></a>${rt}`;
        }

        if (parseInt(data[16]) > 0) rtData(16)

        rtData(3)

        if (c.lvl > -1) {
            if (c.city === data[6]) {
                if (parseInt(data[12]) > 0) {
                    rt = `<a href="javascript:void(0)" onClick="chat.addto(\'${data[1]}\',\'private\')"><img alt="Персонаж сражается" title="Персонаж сражается" src="${c.img2}/i/lock1.gif" width="20" height="15"></a>${rt}`;
                } else {
                    rt = `<a href="javascript:void(0)" onClick="chat.addto(\'${data[1]}\',\'private\')"><img alt="Приват" src="${c.img2}/i/lock.gif" width="20" height="15"></a>${rt}`;
                }
            } else {
                rt = `<img alt="Город" style="padding-right:3px;" onmouseout="top.hic()" onmousedown="top.hic()" onmouseover="top.hi(this,\'Персонаж сейчас в ${data[6]}\',event,3,1,1,2,\'\')" src="${c.img2}/i/city_ico/${data[6]}.gif" width="17" height="15">${rt}`;
            }
        }
        rt += `[${data[2]}]<a href="${c.https}/info/${data[0]}" target="_blank"><img alt="Инф. о ${data[1]}" style="vertical-align:baseline" width="12" height="11" src="${c.img2}/i/inf_${data[5]}.gif" title="Инф. о ${data[1]}" /></a>`;

        if (parseInt(data[11]) > c.time) {
            rt += ` <img alt="Кляп" id="img_molch${data[1]}" width="24" height="15" style="cursor:help" src="${c.img2}/i/sleep2.gif" onmouseout="top.hic()" onmousedown="top.hic()" onmouseover="top.hi(this,\'На персонажа наложено заклятие молчания.<br>Будет молчать еще <span id=\\\'molch${data[0]}\\\'>${this.timeOut(data[11])}</span>\',event,3,1,1,2,\'\');chat.justRefMolch(${data[0]})">`;
            this.addRefMolch(data[0], data[11]);
        }

        if (data[14] !== "") {
            rt += ` <img alt="Травма" width="24" height="15" style="cursor:help" src="${c.img2}/i/travma2.gif" onmouseout="top.hic()" onmousedown="top.hic()" onmouseover="top.hi(this,\'У персонажа ${data[14]}\',event,3,1,1,2,\'\');">`;
        }
        if (parseInt(data[13]) > 0) {
            if (parseInt(data[13]) === 2) {
                rt += ` <a target="main" href="/main.php?atak_user=${data[0]}" title="Кровавое нападение на ${data[1]}"><img alt="" width="13" height="13" src="${c.img2}/i/clear.gif"></a>`;
            } else {
                rt += ` <a target="main" href="/main.php?atak_user=${data[0]}" title="Напасть на ${data[1]}"><img alt="" width="13" height="13" src="${c.img2}/i/curse_attack.gif"></a>`;
            }
        }
        if (parseInt(data[15]) === 1) rt = `<span class=woman >${rt}</span>`
        return rt;
    }, mlch: {},

    justRefMolch: function (id) {
        $(`#molch${id}`).html(this.timeOut(this.mlch[id]));
    },

    addRefMolch: function (id, tm) {
        this.t_all[id] = setTimeout(`chat.refMolch(${id})`, 1000);
        this.mlch[id] = tm;
    },

    refMolch: function (id) {
        clearTimeout(this.t_all[id]);
        if (this.mlch[id] > 0) {
            $(`#molch${id}`).html(this.timeOut(this.mlch[id]));
            this.t_all[id] = setTimeout(`chat.refMolch(${id})`, 1000);
            return
        }
        $(`#img_molch${id}`).remove();
        return delete this.mlch[id], this.t_all;
    },

    fc: function () {
        $('#textmsg').focus();
    },

    timeOut: function (v) {
        let msPerDay = '';
        let dt = new Date();
        dt.setTime((v - c.time) * 1000);

        let m1 = dt.getUTCMonth();
        let d1 = dt.getUTCDay();
        let h1 = dt.getUTCHours();
        let min1 = dt.getUTCMinutes();
        let sec = dt.getUTCSeconds();

        if (m1 > 0) msPerDay = `${m1} мес. `;
        if (d1 > 0 && Math.floor((v - c.time) / (60 * 60 * 24)) === d1) msPerDay = `${d1} д. `;
        if (h1 > 0) msPerDay += `${h1} ч. `;
        if (min1 > 0) msPerDay += `${min1} мин. `;
        if (sec > 0 && msPerDay !== '') msPerDay += `${sec} сек. `;
        if (msPerDay === '') msPerDay = 'меньше минуты.';

        return msPerDay;
    },

    deleteMessage: async function (id, fc) {
        if (fc === 1) top.msgdeleted(id)
        else {
            $(`#msg_${id}`).remove();
            $(`#msg_${id}_sys`).remove();
            if (c.admin > 0) {
                await fetch(`online.php?jack=${c.rnd}&cas${((new Date().getTime()) + Math.random())}`, {
                    method: "POST",
                    delMsg: id
                })
            }
        }
    },

    clear: function () {
        if ($('#textmsg').val() === '') {
            if (confirm('Очистить окно чата?')) {
                if (window.des === 1) {
                    $(`#canal${cb_date[cb_select]}`).html('');
                    $('#textmsg').focus();
                    return
                }
                $('#canal5').html('');
                $('#textmsg').focus();
                return
            }
        }
        return $('#textmsg').val('');
    },

    scrollNow: function (userTrue) {

        var $chat_list = document.getElementById("chat_list")
        var $countChatList = $chat_list.scrollHeight - $chat_list.scrollTop
        if ($countChatList < $chat_list.offsetHeight + 100) {
            return $chat_list.scrollBy(0, $chat_list.scrollHeight)
        }
        if (userTrue) return $chat_list.scrollBy(0, $chat_list.scrollHeight)
    },
    msgcount: 0,

    sendMsg: function (data) { //отсылка сообщения?
        // Если системное сообщение от моба - длина массива == 10
        // Иначе - длина массива == 17
        // все data[] строчные!!!
        // data[0] - какое-то число (или строка 'new')
        // data[1] - видимо номер сообщения в чате за всё время
        // data[2] - число 2, 3, 4, 6, 8, 21 или строка 'delete'
        // data[3] - ник, если покрашенный (отправитель)
        // data[4] - ник, если не покрашенный (получатель)
        // data[5] - само сообщение в чате из инпута
        // data[6] - цвет покраски ника. Если цвета нет - пустая строка
        // data[7] - 0
        // data[8] - 0
        // data[9] - 0
        // data[10] - 0
        // data[11] - 0
        // data[12] - 0 (что-то для админов)
        // data[13] - время
        // data[14] - полная дата
        // data[15] - пустая строка
        // data[16] - 0 (флаг невидимки)

        let to;
        let to2;
        let cls;
        let forYou;
        let msg_see = 1;
        let global_type = 0;

        if (data[5] !== undefined && data[5].substring(0, 7) === 'global:') {
            global_type = 1;
            data[5] = data[5].substring(7);
        }

        if (data[0] === 'new') {
            data[0] = `new_msg_ ${this.newmsg++}`;
        }

        if (data[2] === 'delete') this.deleteMessage(data[0]);
        else if (data['d'] > 0) this.deleteMessage(data['d']);
        else if (data['s'] > 0) this.deleteMessage(data['s']);
        else if (data[0] !== undefined && !top.document.getElementById(`msg_${data[0]}`)) {
            let msg = '';
            if (parseInt(data[0]) !== 0) {
                if (c.admin > 0 && parseInt(data[12]) === 1) {
                    msg += '<small style="color:red;text-decoration:blink"> <b>un</b>active </small>';
                }
                if (data[3] !== '') {
                    let login = data[3];
                    if (data[16] > 0) {
                        login = 'Невидимка';
                    }
                    msg += `[<a href="javascript:void(0)" oncontextmenu="top.infoMenu(\'${login}\',event,\'chat\'); return false;" onClick="chat.addto(\'${login}\',\'to\')">${data[3]}</a>]`;

                    if (chat.ignoreList[data[3]] !== undefined && chat.ignoreList[data[3]] !== null) msg_see = 0;
                }
                if (data[4]) { //кому написали, разбор массива
                    let receiverList = [];
                    let i = 0;
                    let vl = '';

                    forYou = 0;
                    to = '';
                    to2 = '';
                    receiverList = data[4].split(',');

                    while (i !== -1) {
                        if (receiverList[i]) {
                            vl = this.trim(receiverList[i]);
                            if (vl.toLowerCase() === c.login.toLowerCase()) {
                                forYou++;
                            }
                            if (vl.toLowerCase() === c.login.toLowerCase()) {
                                vl = this.trim(data[3]);
                            }
                            if (i > 0) {
                                to += ', ';
                                to2 += ', ';
                            }
                            if (data[3] !== '') {
                                to += `<span style="cursor:pointer;" onclick="chat.addto(\'${vl}\',\'private\');" oncontextmenu="top.infoMenu(\'${this.trim(receiverList[i])}\',event,\'chat\'); return false;">${this.trim(receiverList[i])}</span>`;

                                if (this.trim(receiverList[i].toLowerCase()) !== c.login.toLowerCase()) {
                                    to2 += this.trim(receiverList[i]);
                                } else {
                                    if (data[2] === 2) to2 += this.trim(receiverList[i]);
                                    else to2 += this.trim(vl);
                                }
                            }
                        } else i = -2;
                        i++;
                    }

                    if (parseInt(data[2]) === 6 || parseInt(data[2]) === 8) {
                        let zmlogin = new RegExp("\\[login:(.*?)\\]", "");
                        let reflcd = new RegExp("\\[reflesh_main_zv_priem:(.*?)\\]", "");
                        if (data[5].match(zmlogin) != null) {
                            zmlogin = data[5].match(zmlogin)[1];
                            data[5] = data[5].replace(`[login:${zmlogin}]`, `<a onMouseDown="top.loginGo(\'${zmlogin}\',event);" oncontextmenu="top.infoMenu(\'${zmlogin}\',event,\'chat\'); return false;" title="${zmlogin}" style="cursor:pointer;" onClick="chat.multiAddto(\'${zmlogin}\',\'to\');">${zmlogin}</a>`);
                        }
                        if (data[5].match(reflcd) != null) {
                            reflcd = data[5].match(reflcd)[1];
                            data[5] = data[5].replace(`[reflesh_main_zv_priem:${reflcd}]`, '');
                        }
                    }

                    //Собираем массив кому адресовано сообщение
                    if (parseInt(data[2]) === 6) {
                        //личная системка, внимание
                        msg += ' <span style="color:red">Внимание!</span> ';
                    } else if (parseInt(data[2]) === 2) {
                        if (forYou > 0) {
                            msg += ` <span style="color:${data[6]}"><b>to [${to2}]</b></span>`;
                        } else {
                            msg += ` <span style="color:${data[6]}">to [${to2}]</span>`;
                        }
                    } else if (parseInt(data[2]) === 3) {
                        if (this.trim(data[3].toLowerCase()) === c.login.toLowerCase()) {
                            forYou++;
                        }
                        if (data[3] !== '') {
                            if (data[4] === 'klan' && parseInt(data[2]) === 3) {
                                msg += ' <span class="klan"><span style="cursor:pointer" onclick="chat.multiaddto(\'klan\',\'private\');">private [klan]</span></span>';
                            } else {
                                msg += ` <span class="private"><span style="cursor:pointer" onclick="chat.multiaddto(\'${to2}\',\'private\');">private [ </span>${to}<span style="cursor:pointer" onclick="chat.multiAddto(\'${to2}\',\'private\');"> ]</span></span>`;
                            }
                        }
                    }
                }
                msg += ' ';
            }

            data[5] = data[5].replaceAll("[s1;]", '"');
            data[5] = data[5].replaceAll("[s2;]", '\'');
            data[5] = data[5].replaceAll("[s3;]", '<');
            data[5] = data[5].replaceAll("[s4;]", '>');

            if ($.cookie('chatCfg2')) {
                data[5] = chat.testSmile(data[5]);
            }

            let voiceMessageReg = data[5].match(/audio_[0-9]{10}[.]mp3/g)
            let color = 'black';
            if (data[6] !== 'Black' && data[6] !== '') {
                color = data[6];
            }

            if (voiceMessageReg) {
                msg += `<span style="color: ${color};">
							<audio id="audioPlayerChat" controls style="width:300px;max-width:400px;height:200px">
								<source src="/audio/${voiceMessageReg[0]}" type="audio/mp3">
							</audio>
						</span>`;
            } else {
                if (data.length === 10) {
                    msg += `<span style="color: ${color};">${data[5]}</span>`;
                } else {
                    msg += `<span style="color: ${color};">${chat.regexpURL_true(data[5])}</span>`
                }
            }

            if (parseInt(data[2]) === 21) {
                //e text
                let text = `[loginfrom] ${data[5]}`;
                const ftps = `<i><a href="javascript:void(0)" oncontextmenu="top.infoMenu(\'${data[3]}\',event,\'chat\'); return false;" onClick="chat.addto(\'${data[3]}\',\'to\')">${data[3]}</a></i>`;
                let mblogin = new RegExp("\\[login:(.*?)\\]", "");
                text = text.replace('[loginfrom]', ftps);
                if (text.match(mblogin) != null) {
                    mblogin = text.match(mblogin)[1];
                    text = text.replace(`[login:${mblogin}]`, `<i><a href="javascript:void(0)" oncontextmenu="top.infoMenu(\'${mblogin}\',event,\'chat\'); return false;" onClick="chat.addto(\'${mblogin}\',\'to\')">${mblogin}</a></i>`);
                }
                msg = `<i>${text}</i>`;
            }

            if (parseInt(data[1]) > 0) {
                let td = new Date((parseInt(data[1])) * 1000);
                td = [td, null, null, null];
                td[1] = td[0].getHours();
                td[2] = td[0].getMinutes();
                td[3] = td[0].getSeconds();
                td[4] = td[0].getDay();
                td[5] = td[0].getMonth();
                td[6] = td[0].getFullYear();

                let j = 1;
                while (j < 6) {
                    if (td[j] < 10) td[j] = `0${td[j]}`;
                    j++;
                }

                cls = forYou > 0 ? 'date2' : 'date';

                if (parseInt(data[11]) > 0) {
                    msg = `<span style="cursor:help;color:red;" onmouseout="top.hic()" onmousedown="top.hic()" onmouseover="top.hi(this,\'Отправитель наказан за нарушение правил общения<br>Отключить отображение подобных сообщений можно в настройках чата.\',event,3,1,1,3,\'\')"> <b>!</b> </span>${msg}`;
                }
                if (parseInt(data[10]) > 0) {
                    msg = `<span style="cursor:help;color:green;" onmouseout="top.hic()" onmousedown="top.hic()" onmouseover="top.hi(this,\'Это глобальное сообщение, оно может быть отправлено из любой локации<br>Отключить отображение подобных сообщений можно в настройках чата.\',event,3,1,1,3,\'\')"> <b>G</b> </span>${msg}`;
                }

                let msg22 = '<span ';
                if (c.admin > 0) {
                    msg22 += `oncontextmenu="chat.deleteMessage(${data[0]});
						return false;" `;
                }

                // ОПТИМИЗАЦИЯ!!! (ничего не понятно, но очень интересно!)
                if (parseInt(data[8]) === 2) {
                    if (data[14] === undefined || data[14] === null) {
                        data[14] = '--:--';
                    }
                    if (data[14] === '--:--' && parseInt(data[1]) > 0) {
                        data[14] = data[1];
                        const date14 = new Date(parseInt(data[14]) * 1000);
                        data[14] = `${date14.getHours()}:${date14.getMinutes()}`;
                    }
                    msg22 += `class="${cls}">${data[14]}</span> `;
                } else {
                    if (data[13] === undefined || data[13 === null]) {
                        data[13] = '--:--';
                    }
                    if (data[13] === '--:--' && parseInt(data[1]) > 0) {
                        data[13] = data[1];
                        const date13 = new Date(parseInt(data[13]) * 1000);
                        data[13] = `${date13.getHours()}:${date13.getMinutes()}`;
                    }
                    msg22 += `class="${cls}">${data[13]}</span> `;
                }
                msg = msg22 + msg;
            }
            this.msgcount++;

            msg = `<span class="m0c1" id="msg_${data[0]}">${msg}<br></span>`;

            if (msg_see === 1) {
                if (this.trim(data[3].toLowerCase()) === c.login.toLowerCase() || forYou === 1) {
                    if (data[15]) {
                        this.feerverk(data[15]);
                        this.feerverk(data[15]);
                    }

                    if (window.des === 1) {
                        if (parseInt(data[2]) < 4 || global_type === 1) {
                            //Обычный чат
                            chat.textColor_Work('#canal5', msg, 5)
                            if (global_type === 1) {
                                //Системный чат
                                chat.textColor_Work('#canal4', msg, 4)
                            }
                        } else {
                            //Системный чат
                            chat.textColor_Work('#canal4', msg, 4)
                            chat.textColor_Work('#canal5', msg, 5)
                        }
                    } else {
                        const htmlElementsMessage = new DOMParser().parseFromString(msg, "text/html").getElementsByTagName("span")[0];
                        document.getElementById('canal5').append(htmlElementsMessage)
                        //$('#canal5').html(`${$('#canal5').html()}${msg}`);
                    }
                }
            }
            this.scrollNow(false);
        }
    },

    /**
     * @param {*} canal
     * @param {string} messg
     * @param {number} count
     */
    textColor_Work: function (canal, messg, count) {
        const htmlElementsMessage = new DOMParser().parseFromString(messg, "text/html").getElementsByTagName("span")[0];
        const idCanal = canal.match(/canal[0-9]/);
        const idCanal0 = document.getElementById(idCanal[0]);
        if (!idCanal0) $(canal).html(`${$(canal).html()}${messg}`)
        else idCanal0.append(htmlElementsMessage)
        top.blueTextSee(count);
    },
    testKey: function (m, v) {
        let i = 0, r = v;
        v = false;
        while (i !== -1) {
            if (m[i] !== undefined) {
                if (m[i] === r) {
                    v = i;
                    i = -2;
                }
            } else i = -2;
            i++;
        }
        return v;
    },
    isNumber: function (s) {
        return s = !isNaN(s)
    },
    testSmile: function (txt) {
        let txr = txt.split(':');
        let i = 1,
            j = 0,
            smid = 0;
        let imsml;
        while (i <= txr.length) {
            if (txr[i] !== undefined) {
                smid = this.testKey(sml, txr[i]);
                imsml = txr[i].split('-');
                if (((smid !== false || smid === 0) && this.isNumber(txr[i]) !== true) || (imsml !== undefined && imsml[0] === '%usersmile%')) {
                    if (j < 3 && this.isNumber(sml[smid]) !== true && (sml[smid] !== undefined || imsml[0] === '%usersmile%')) {
                        // тут
                        if (imsml[0] === '%usersmile%') {
                            txt = txt.replace(`\:%usersmile%-${imsml[1]}\:`, `<img alt="Именной смайлик" src="${c.img2}/i/smile/${(imsml[1].toLowerCase())}.gif" title="Именной смайлик">`);
                        } else {
                            txt = txt.replace(`\:${txr[i]}\:`, `<img alt="Смайлик" src="${c.img2}/i/smile/${(txr[i].toLowerCase())}.gif" style="cursor:pointer" width="${sml[smid + 1]}" height="${sml[smid + 2]}" onclick="chat.addSmile(\'${(txr[i].toLowerCase())}\')">`);
                        }
                        j++;
                    }
                }
            }
            i++;
        }
        return txt;
    },

    regexpURL_true(str) {
        let key;
        let finallyStr = str;
        const arrURL = str.match(/[-a-zA-Z0-9А-я@:;%_\+.~#?&\/=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:;%_\+.~#?&\/=]*)?/gi);
        const domainsRUS = str.match(/[a-zA-Z0-9А-я-]+[.,:;\/?!@#$%^&*()_+-`'"|]+(ком|ру|юа)+/gi);
        const domainsEN = str.match(/[a-zA-Z0-9А-я-]+[ .,:;\/?!@#$%^&*()_+-`'"|]+\b(c[o]+m|ua|r[u]{1,}|net|cc)+\b/gi);
        const regexpForbiddenWords = /\b(old|rebk|combatz|sbkru|urlidru|real|obk2com|bitly|owly|cruelbk|likebk|isgd|fightbk|my-|my-combatc|mbk|anti|ws|s3s|bakubk|idealbk|bespridel|perbk|online|cf|artovik|probk|twar|oyy|krbk|habk|timebk|sbi|eea|sokbk|inbk|refbk|bk20|analogbk|monstrbk|awOev|cruelcity|tlgr|vntr|supbk|cu|tn|af|svel|kwn|krati|ohh|gHjq|ogw|lur|lmy|idz|hop|eee|bestbk|pixl|ssylka|f34r|exelendbk|9tl|qil|2t9|NjQ0|net|bishky|getl|ixz|nvm|prok|5char|tdl|pick|jj|FORM|zik|biz|sns|snsh|pnut|pb8|short|legendabk|prnt|coombats|tiny|orDеNВК|nbk|orden|club|utf|ubit|clc|wocmaet|wiecej|ourl|3le|NikY|fia|iop|ordenbk|sW|sw|so|xcombat|world|death|legendbattles|plu|dbe|kombatz|over|dek|ci8|goldbk|Zeus|nxt|is|Ndpx|pw|kutt|JovdeT|cort|rexno|bly|tFXUQ|fyi|yf0ck|lst|numl|yx5ru|vhGyJ|GEROY|MOYEP|fuck|lnnk|dhaF|oko|x8b4q|Ru2q|psce|yx5|bg9xH|Rich|Lands|yx|bgH|huxoqM|bgxH|Nextbk|idea|PLAYBK|dance|coronabk|hitbk|ttpham|RСомbаts|dedmoroz|wmj9bz|king|ZEBK|urlgo|ZniD)+\b/g;

        finallyStr = str.replace(regexpForbiddenWords, () => "")

        const allURL = new Set();

        if (domainsEN) for (key of domainsEN) {
            if (!key.match(/^[A-zА-я0-9]+$/i)) allURL.add(key)
        }

        if (domainsRUS) for (key of domainsRUS) {
            if (!key.match(/^[A-zА-я0-9]+$/i)) allURL.add(key)
        }
        if (arrURL) for (key of arrURL) allURL.add(key)
        if (!allURL.size) return finallyStr

        let falseURL = false;

        if (falseURL) return "В сообщении присутствуют запрещенные ссылки."

        finallyStr = finallyStr.replace(
            /(<img src=")?(https?:\/\/)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z()]{1,6}\b([-a-zA-Z0-9()@:%_;\+.~#?&\/\/=]*)/gi,
            (match) => {
                if (/^(<img src=")/.test(match)) {
                    return match
                } else if (match.match(/new-combats\.com/i)) return match
                else if (match.match(/inf\.php/i)) return match
                else if (/^(https?:\/\/)/.test(match)) {
                    return `<a class="URL-a" href="${match}" target="_blank">${match}</a>`
                } else {
                    return `<a class="URL-a" href="https://${match}" target="_blank">${match}</a>`
                }
            }
        )

        finallyStr = finallyStr.replace(regexpForbiddenWords, () => "")
        return finallyStr
    },

    trim: function (s) {
        return this.rtrim(this.ltrim(s));
    },
    ltrim: function (s) {
        return s.replace(/^\s+/, '');
    },
    rtrim: function (s) {
        return s.replace(/\s+$/, '');
    },
    multiaddto: function (users, tp) {
        const arr = users.split(',');
        let i = arr.length;
        while (i >= 0) {
            if (arr[i] !== undefined) {
                this.addto(arr[i], tp);
            }
            i--;
        }
    },
//Генерируем данные
    // data - JSON С УЖЕ СУЩЕСТВУЮЩИМИ ПОЛЯМИ!!!! но можеть быть NULL
    // data.rn = online room name
    // data.list = online users list
    /**
     * Генератор данных чата.
     * @param {json|null} data
     * @param {number} [prs]
     */
    genchatData: function (data, prs) {
        let i;
        //console.log(JSON.parse(data));
        if (prs === 1 && typeof data === "string") { // срабатывает если текст пришёл от игрока о_О
            data = JSON.parse(data);
        }

        if (data === null && this.saveData !== null) {
            data = this.saveData;
            data.js = '';
            data.rn = undefined;
            data.key = undefined;
        }
        //Получаем сообщение
        //Приваты судя не прилетают.
        if (data.msg !== undefined) {
            const ms = JSON.parse(data.msg);

            //console.log(ms)
            // 0:"4396466" id?
            // 1:"1686338170" timestamp?
            // 2:"2" type?
            // 3:"Merlin" who
            // 4:"Дмитрий" whom
            // 5:"  кому " message
            // 6:"" ??
            // 7:"0" ??
            // 8:"0" ??
            // 9:"0" ??
            // 10:"0" ??
            // 11:"0" ??
            // 12:"0" ??
            // 13:"22:16" time
            // 14:"09.06.2023 22:16" datetime
            // 15:"" ??
            // 16:"0" ??

            if (ms['ld'] > this.msg_id) this.msg_id = ms['ld'];
            i = 0;
            while (i <= ms['id']) {
                if (ms[`m${i}`] !== undefined) {
                    // console.group(ms[`m${i}`])
                    this.sendMsg(ms[`m${i}`]);
                }
                i++;
            }
        }
        //Если есть JS
        if (data.js !== '') eval(data.js);
        if (data.rnd !== undefined) c.rnd = data.rnd;
        if (data.rn !== undefined && data.rn !== null) {// делаем заголовок людей онлайн

            let showAllUsersCheckbox = document.getElementById('chcf10');
            let showRoomName = document.getElementById('roomName');
            if (showAllUsersCheckbox.checked) {
                showRoomName.innerHTML = `${data.rn}<br><small>Общий онлайн: ${data.xu}</small>`
            } else {
                showRoomName.innerHTML = `${data.rn} (${data.xu})`
            }

        }
        if (data.key !== undefined) this.key = data.key;

        if (data.list !== undefined && data.list !== null) { //делаем список людей онлайн
            let jj;
            i = 0;

            /*{
            "0":["27318846","Дмитрий","8","50","0","capitalcity","capitalcity","9","","","0","0","0","0","","0",""],
            "1":["27205347","Merlin","8","1.99","Radminion","capitalcity","capitalcity","349","","","0","-4","0","0","","0",""]
            } */

            let ji = JSON.parse(data.list),
                onll = '',
                fSort = {},
                flSort = {},
                flSortSee = '"Служба Поддержки"';
            //сортируем данные
            while (i <= data.xu) {
                if (ji[i] !== undefined) {
                    jj = ji[i][1].toLowerCase(); //по логину
                    fSort[jj] = i;
                    flSort[i] = jj;
                    flSortSee += `,"${flSort[i]}"`;
                }
                i++;
            }
            flSortSee = eval(`[${flSortSee}]`);
            if ($.cookie('chatCfg9') === 1) flSortSee.sort(game.sort2);
            else flSortSee.sort(game.sort1);
            //Выводим данные
            i = 0;
            let onll_alh = ''; //список алхимиков?
            while (i <= data.xu) {
                if (fSort[flSortSee[i]] !== undefined) {
                    if (ji[fSort[flSortSee[i]]][3] === 50) {
                        onll_alh += `<span class="m0c1">${this.gUser(ji[fSort[flSortSee[i]]], true)}</span>`;
                    } else {
                        onll += `<span class="m0c2">${this.gUser(ji[fSort[flSortSee[i]]], true)}</span>`;
                    }
                }
                i++;
            }
            let onlineList = document.getElementById('onlist');
            onlineList.innerHTML = `${onll_alh}${onll}`
        }
    },

    voiceMessage: function () {
        flagVoice = !flagVoice
        if (flagVoice) return chat.startRecording(flagVoice)
        return chat.startRecording(flagVoice)
    },
    startRecording: function (flag) {
        if (flag) {
            if (navigator.mediaDevices.getUserMedia) {
                const pVoiceMessages = document.getElementById("timerVoiceMess");
                pVoiceMessages.hidden = false

                const constraints = {
                    audio: true
                };

                const onSuccess = function (stream) {
                    mediaRecorder = new MediaRecorder(stream);
                    mediaRecorder.start()
                    timerSecMessage = 0
                    let timerMlSecMessage = 1;

                    setIntervalSound = setTimeout(function tickMlsec() {
                        if (timerSecMessage === 60) {
                            flagVoice = !flagVoice

                            chat.styleRecordButton({
                                textButt: "Запись",
                                color: "",
                                backgroundColor: "",
                                allSetting: true
                            })
                            return
                        }

                        if (timerSecMessage > 9) pVoiceMessages.textContent =
                            `00:${timerSecMessage},${timerMlSecMessage}`
                        else pVoiceMessages.textContent =
                            `00:0${timerSecMessage},${timerMlSecMessage}`

                        if (++timerMlSecMessage > 9) {
                            timerMlSecMessage = 0
                            ++timerSecMessage
                        }

                        setIntervalSound = setTimeout(tickMlsec, 100)
                    }, 100)

                    chat.styleRecordButton({
                        textButt: "Завершить",
                        color: "rgb(165, 0, 0)",
                        backgroundColor: "rgba(255, 0, 0, 0.083)",
                        allSetting: false
                    })

                    mediaRecorder.ondataavailable = function (e) {
                        chunksVoice.push(e.data)
                    }
                };

                const onError = function (err) {
                    console.log(`Ошибка: ${err}`)
                    alert("Ошибка, проверьте подключение или работоспособность микрофона!")
                };

                navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError)
            } else console.log("Аудиосообщения не поддерживаются вашим браузером!")

            return
        }


        chat.styleRecordButton({
            textButt: "Запись",
            color: "",
            backgroundColor: "",
            allSetting: true
        })
    },
    styleRecordButton: function (objArgs) {
        const {
            textButt,
            color,
            backgroundColor,
            allSetting
        } = objArgs;

        const buttonVoiceMess = document.getElementById("record");
        const pVoiceMessages = document.getElementById("timerVoiceMess");


        if (allSetting) {
            clearInterval(setIntervalSound)

            pVoiceMessages.textContent = "00:00,0"
            buttonVoiceMess.textContent = textButt
            buttonVoiceMess.style.color = color
            buttonVoiceMess.style.backgroundColor = backgroundColor
            pVoiceMessages.hidden = allSetting

            if (!mediaRecorder) return alert("Ошибка, проверьте подключение или работоспособность микрофона! Если Вы подключили микрофон и всё работает - перезагрузите страницу <F5> и попробуйте снова!")
            mediaRecorder.stop()
            mediaRecorder.onstop = function () {
                chat.mediaRecorderStop()
            }

            mediaRecorder.stream.getAudioTracks().forEach(function (track) {
                track.stop()
            })
            return
        }

        const footerTop = document.getElementsByClassName("allChat")[1].offsetTop;
        const tdLeft = buttonVoiceMess.parentElement.offsetLeft;
        const VoiceWidth = buttonVoiceMess.clientWidth;
        const pVoiceWidth = pVoiceMessages.clientWidth;

        pVoiceMessages.style.top = `${footerTop - 30}px`
        pVoiceMessages.style.left = `${(tdLeft + 9) + ((VoiceWidth - pVoiceWidth) / 2)}px`

        buttonVoiceMess.textContent = textButt
        buttonVoiceMess.style.color = color
        buttonVoiceMess.style.backgroundColor = backgroundColor
        pVoiceMessages.hidden = allSetting
    },
    mediaRecorderStop: function () {
        if (timerSecMessage < 2) {
            chunksVoice.pop()
            return
        }

        const blob = new Blob(chunksVoice, {
            'type': 'audio/mp3; codecs=opus'
        });

        chunksVoice.pop()

        const fileReader = new FileReader();
        fileReader.readAsArrayBuffer(blob)

        fileReader.onload = async function () {
            const inputWindow = document.getElementById("textmsg");
            const arrayBuffer = fileReader.result;

            const response = await fetch("/audio.php", {
                method: "POST",
                body: arrayBuffer
            });
            const timeStampFile = await response.text();

            inputWindow.value =
                `${inputWindow.value} audio_${timeStampFile}.mp3`
            await chat.subSend()
        }
    }
};