hwmSetsMaster

Меню наборов армии, навыков и оружия. Смена фракции.

目前為 2023-11-29 提交的版本,檢視 最新版本

// ==UserScript==
// @name           hwmSetsMaster
// @author         Tamozhnya1
// @namespace      Tamozhnya1
// @description    Меню наборов армии, навыков и оружия. Смена фракции.
// @version        8.7
// @encoding        utf-8
// @include        *heroeswm.ru/*
// @include        *lordswm.com/*
// @exclude        */rightcol.php*
// @exclude        */ch_box.php*
// @exclude        */chat*
// @exclude        */ticker.html*
// @exclude        */frames*
// @exclude        */brd.php*
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_deleteValue
// @grant 		   GM.xmlHttpRequest
// @license        MIT
// ==/UserScript==

const playerIdMatch = document.cookie.match(/pl_id=(\d+)/);
if(playerIdMatch) {
    var PlayerId = playerIdMatch[1];
}
let Fraction;
const windowObject = window.wrappedJSObject || unsafeWindow;
const isEn = document.documentElement.lang == "en";
const Strings = {
        "ru": {
            Army: ustring("Армия"),
            Save: ustring("Сохранить"),
            Add: ustring("Добавить"),
            AddCurrent: ustring("Добавить текущий"),
            SetName: ustring("Наименование набора"),
            Delete: ustring("Удалить"),
            Talents: ustring("Навыки"),
            Weapon: ustring("Оружие"),
            RemoveAll: ustring("Снять все"),
            EnterJpg: "enter0.jpg",
            SignInTitle: "Войти",
            Castle: "Замок",
            Task: ustring("Задание"),
            Apply: ustring("Применить"),
            EnterButtonValue: ustring("ВОЙТИ"),
            AvailablePoints: "Свободных очков",
            AvailableTalentPoints: "Свободных очков от навыка",
            IncreaseManyPointsTooltip: "Введите число от ${minValue} до ${maxValue}. Нажмите Tab."
        },
        "en": {
            Army: "Army",
            Save: "Save",
            Add: "Add",
            AddCurrent: "Add current",
            SetName: "Set name",
            Delete: "Delete",
            Talents: "Talents",
            Weapon: "Weapon",
            RemoveAll: "Un-equip all",
            EnterJpg: "enter0_eng.jpg",
            SignInTitle: "Sign in",
            Castle: "Castle",
            Task: "Task",
            Apply: "Apply",
            EnterButtonValue: "ENTER",
            AvailablePoints: "Available points",
            AvailableTalentPoints: "Available talent points",
            IncreaseManyPointsTooltip: "Enter number from ${minValue} to ${maxValue}. Press Tab."
        }
    };
const LocalizedString = Strings[document.documentElement.lang || "ru"];
let userName;
const isMobileInterface = document.querySelector("div#btnMenuGlobal") ? true : false;
const isMobileDevice = mobileCheck(); // Там нет мышки
const isNewInterface = document.querySelector("#main_top_table") ? false : true;
const isNewPersonPage = document.querySelector("div#hwm_no_zoom") ? true : false;

ensureGmMethods();
getUserName();
getFraction();

/************************************************************************************************************/
const weaponSetsPreferences = {
    menuTitle: LocalizedString.Weapon,
    setReferencePage: "inventory.php",
    sets: new Array(),
    menuItems: {},
    currentMenuItem: undefined,
    refreshingPages: "home.php;inventory.php;pl_info.php",
    updatePanelHandler: async function() {
        if(location.pathname == '/home.php') {
            let updatePanel, newUpdatePanel;
            const doc = await getRequest(`/home.php`);
            if(isNewPersonPage) {
                updatePanel = document.querySelector("div#inv_doll_stats");
                newUpdatePanel = doc.querySelector("div#inv_doll_stats");
            } else {
                updatePanel = getParent(document.querySelector("div.arts_info.shop_art_info"), "table");
                newUpdatePanel = getParent(doc.querySelector("div.arts_info.shop_art_info"), "table");
            }
            Array.from(newUpdatePanel.querySelectorAll("img.show_hint")).forEach(x => x.setAttribute("title", x.getAttribute("hint").replace(/<br \/>/g, "\n")));
            updatePanel.innerHTML = newUpdatePanel.innerHTML;
            
            const statsUpdatePanel = isNewPersonPage ? document.getElementById("home_css_stats_wrap_div") : getParent(document.querySelector("img[src*='attr_attack']"), "table", 2);
            const newStatsUpdatePanel = isNewPersonPage ? doc.getElementById("home_css_stats_wrap_div") : getParent(doc.querySelector("img[src*='attr_attack']"), "table", 2);
            statsUpdatePanel.innerHTML = newStatsUpdatePanel.innerHTML;

            return true;
        }
        if(location.pathname == '/pl_info.php') {
            if(getUrlParamValue(location.href, "id") == PlayerId) {
                const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);
                const updatePanel = getParent(document.querySelector("div[class^='slot']"), "div");
                const newUpdatePanel = getParent(doc.querySelector("div[class^='slot']"), "div");
                Array.from(newUpdatePanel.querySelectorAll("img.show_hint")).forEach(x => x.setAttribute("title", x.getAttribute("hint").replace(/<br \/>/g, "\n")));
                updatePanel.innerHTML = newUpdatePanel.innerHTML;
                
                const statsUpdatePanel = getParent(document.querySelector("img[src*='attr_attack']"), "table");
                const newStatsUpdatePanel = getParent(doc.querySelector("img[src*='attr_attack']"), "table");
                statsUpdatePanel.innerHTML = newStatsUpdatePanel.innerHTML;
            }
            return true;
        }
        return false;
    },
    refreshingConditions: function() {
        if(/map.php/.test(location.href)) {
            const bolds = document.querySelectorAll("b");
            for(const bold of bolds) {
                if(bold.innerText.includes(LocalizedString.Task)) {
                    return true;
                }
            }
            const neut_right_block = document.querySelector("#neut_right_block");
            if(neut_right_block) {
                return true;
            }
        }
        return false;
    },
    initSetsApplyAction: function() {
        this.sets.length = 0;
        this.sets.push({ number: 0, name: LocalizedString.RemoveAll, method: "GET", url: "inventory.php?all_off=100" });
        const weaponSets = JSON.parse(GM_getValue(`WeaponSets${PlayerId}`, "{}"));
        for(const setNumber of Object.keys(weaponSets)) {
            this.sets.push({ number: setNumber, name: weaponSets[setNumber], method: "GET", url: `inventory.php?all_on=${setNumber}`, headers: null });
        }
    },
    getCurrentSetName: function() { return `WeaponSet${PlayerId}`; },
    onPageLoad: function() {
        if(/inventory.php/.test(location.href)) {
            const undressDiv = document.querySelector("div[id ='undress_all_div']");
            addSetChangerListener(undressDiv, this, 0);

            const setDivs = document.querySelectorAll("div[set_div_id]"); // Если setDivs.length = 0, то ничего не делаем - мы в заявке на бой
            if(setDivs.length > 0) {
                const weaponSets = {};
                for(const setDiv of setDivs) {
                    const setNumber = setDiv.getAttribute("set_div_id");
                    if(setDiv.hasAttribute("onclick")) {
                        weaponSets[setNumber] = setDiv.innerText;
                        addSetChangerListener(setDiv, this, setNumber);
                    }
                }
                GM_setValue(`WeaponSets${PlayerId}`, JSON.stringify(weaponSets));
            }
        }
        this.findSetChangersAndAddListener();
    },
    findSetChangersAndAddListener: function() {
        let setRefs = document.querySelectorAll("a[href^='inventory.php?all_off=100']");
        for(const setRef of setRefs) {
            addSetChangerListener(setRef, this, 0);
        }
        setRefs = document.querySelectorAll("a[href*='inventory.php?all_on=']");
        for(const weaponSetReference of setRefs) {
            let setNumber = weaponSetReference.getAttribute("href").split("all_on=")[1];
            addSetChangerListener(weaponSetReference, this, setNumber);
        }
    }
};
/***********************************************************************************************************/
const skillSetsPreferences = {
    menuTitle: LocalizedString.Talents,
    setReferencePage: "skillwheel.php",
    sets: new Array(),
    menuItems: {},
    currentMenuItem: undefined,
    refreshingPages: "skillwheel.php;pl_info.php;home.php",
    updatePanelHandler: async function() {
        if(location.pathname == '/home.php') {
            const doc = await getRequest(`/home.php`);
            const statsUpdatePanel = isNewPersonPage ? document.getElementById("home_css_stats_wrap_div") : getParent(document.querySelector("img[src*='attr_attack']"), "table", 2);
            const newStatsUpdatePanel = isNewPersonPage ? doc.getElementById("home_css_stats_wrap_div") : getParent(doc.querySelector("img[src*='attr_attack']"), "table", 2);
            statsUpdatePanel.innerHTML = newStatsUpdatePanel.innerHTML;
            getPerksToHomeCore();
            return true;
        }
        if(location.pathname == '/pl_info.php') {
            if(getUrlParamValue(location.href, "id") == PlayerId) {
                const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);
                const statsUpdatePanel = getParent(document.querySelector("img[src*='attr_attack']"), "table");
                const newStatsUpdatePanel = getParent(doc.querySelector("img[src*='attr_attack']"), "table");
                statsUpdatePanel.innerHTML = newStatsUpdatePanel.innerHTML;

                const perksUpdatePanel = getParent(document.querySelector("a[href^='showperkinfo.php']"), "table", 2);
                const newPerksUpdatePanel = getParent(doc.querySelector("a[href^='showperkinfo.php']"), "table", 2);
                Array.from(newPerksUpdatePanel.querySelectorAll("img.show_hint")).forEach(x => x.setAttribute("title", x.getAttribute("hint").replace(/<br \/>/g, "\n")));
                perksUpdatePanel.innerHTML = newPerksUpdatePanel.innerHTML;
            }
            return true;
        }
        return false;
    },

    onPageLoad: function() {
        const setRefs = document.querySelectorAll("a[href^='skillwheel.php?setuserperk']"); // skillwheel.php?setuserperk=1&prace=4&buildid=5 // skillwheel.php?rand=1&setstats=1&param0=20&param1=8&param2=0&param3=2
        const pageSets = {};
        for(const setRef of setRefs) {
            pageSets[setRef.innerHTML] = getUrlParamValue(setRef.href, "buildid");
            addSetChangerListener(setRef, this, parseInt(getUrlParamValue(setRef.href, "buildid")));
        }
        GM_setValue(`SkillSets${PlayerId}Fraction${Fraction}`, JSON.stringify(pageSets));
    },
    initSetsApplyAction: function () {
        //console.log(GM_getValue(`SkillSets${PlayerId}Fraction${Fraction}`));
        const skillSets = JSON.parse(GM_getValue(`SkillSets${PlayerId}Fraction${Fraction}`, "{}"));
        this.sets = Object.keys(skillSets).map(x => { return { number: parseInt(skillSets[x]), name: x, method: "GET", url: `/skillwheel.php?setuserperk=1&prace=${Fraction}&buildid=${skillSets[x]}` }; });
    },
    getCurrentSetName: function() { return `CurrentSkillSet${PlayerId}Fraction${Fraction}`; }
};
/************************************************************************************************************/
const armySetsPreferences = {
    menuTitle: LocalizedString.Army,
    setReferencePage: "army.php",
    sets: new Array(),
    menuItems: {},
    currentMenuItem: undefined,
    refreshingPages: "home.php;army.php;pl_info.php",
    updatePanelHandler: async function() {
        if(location.pathname == '/home.php') {
            let updatePanel, newUpdatePanel;
            const doc = await getRequest(`/home.php`);
            if(isNewPersonPage) {
                updatePanel = document.querySelector("div.home_pers_army");
                newUpdatePanel = doc.querySelector("div.home_pers_army");
                Array.from(newUpdatePanel.querySelectorAll("div.castle_creature54.show_hint")).forEach(x => x.setAttribute("title", x.getAttribute("hint")));
            } else {
                updatePanel = document.querySelector("center > div > div.cre_creature72").parentNode;
                newUpdatePanel = doc.querySelector("center > div > div.cre_creature72").parentNode;
                Array.from(newUpdatePanel.querySelectorAll("div.cre_creature72 img")).forEach(x => x.setAttribute("title", x.getAttribute("hint")));
            }
            updatePanel.innerHTML = newUpdatePanel.innerHTML;
            return true;
        }
        if(location.pathname == '/pl_info.php') {
            if(getUrlParamValue(location.href, "id") == PlayerId) {
                const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);
                const newUpdatePanel = doc.querySelector("center > div > div.cre_creature72").parentNode;
                Array.from(newUpdatePanel.querySelectorAll("div.cre_creature72 img")).forEach(x => x.setAttribute("title", x.getAttribute("hint")));

                const updatePanel = document.querySelector("center > div > div.cre_creature72").parentNode;
                updatePanel.innerHTML = newUpdatePanel.innerHTML;
            }
            return true;
        }
        // Скрипты ломаются
        // if(location.pathname == '/army.php') {
            // const doc = await getRequest(`/army.php`);
            // const newUpdatePanel = doc.querySelector("div#army_info_div");
            // const updatePanel = document.querySelector("div#army_info_div");
            // updatePanel.innerHTML = newUpdatePanel.innerHTML;
            // return true;
        // }
        return false;
    },
    setsTable: null,

    getCurrentSetName: function() { return `ArmySet${PlayerId}Fraction${Fraction}`; },
    initSetsApplyAction: function () {
        //GM_deleteValue(`ArmySets${PlayerId}Fraction${Fraction}`);
        if(!GM_getValue(`ArmySets${PlayerId}Fraction${Fraction}`)) {
            let savedSetIdsStr = GM_getValue(`${userName}r${Fraction}_savedArmySetIds`);
            let setIds = new Array();
            if(savedSetIdsStr) {
                setIds = savedSetIdsStr.split("|");
            }
            const armySetsData = {};
            for(const setId of setIds) {
                if(setId == "") {
                    continue;
                }
                const setStr = GM_getValue(`${userName}r${Fraction}_savedArmySet${setId}`);
                if(!setStr) {
                    continue;
                }
                let setArr = setStr.split("|");
                armySetsData[setId] = [setArr[7]].concat(setArr.slice(0, 7)).join("|");
            }
            GM_setValue(`ArmySets${PlayerId}Fraction${Fraction}`, JSON.stringify(armySetsData));
        }
        //console.log(GM_getValue(`ArmySets${PlayerId}Fraction${Fraction}`, "{}"));
        const armySetsData = JSON.parse(GM_getValue(`ArmySets${PlayerId}Fraction${Fraction}`, "{}"));
        const armySets = Object.keys(armySetsData).map(x => { const data = armySetsData[x].split("|"); return { Id: x, Name: data[0], Army: data.slice(1).map(y => Number(y)) } });
        this.sets = armySets.map(x => { return { number: parseInt(x.Id), name: x.Name, title: x.Army.join("+"), method: "POST", url: "/army_apply.php", data: x.Army.map((x, i) => `countv${i + 1}=${x}`).join("&"), contentType: "application/x-www-form-urlencoded" }; });
        if(/\/army.php/.test(location.href)) {
            this.drawSetsTable(armySets);
        }
    },
    drawSetsTable: function(armySets) {
        const isInit = this.setsTable ? false : true;
        let container;
        if(isInit) {
            const hwm_for_zoom = document.getElementById("hwm_for_zoom");
            container = document.body;
            if(!isMobileInterface) {
                container = addElement("center", hwm_for_zoom);
            }
            const tableStyle = "background-color: #959595; border: 1px solid #f5c137; table-layout: fixed;" + (isMobileInterface ? "margin: 0 0 0 50px;" : "");
            this.setsTable = addElement("table", container, { style: tableStyle });
        } else {
            this.setsTable.innerHTML = "";
            container = this.setsTable.parentNode;
        }

        const cellWidths = [100, 60, 60, 60, 60, 60, 60, 60, 10, 10];
        for(const cellWidth of cellWidths) {
            this.setsTable.innerHTML += `<col style="width: ${cellWidth}px;" />`;
        }
        this.drawTableHeader();
        for(const armySet of armySets) {
            this.drawSetRow(armySet);
        }
        if(isInit) {
            const saveButton = addElement("input", container, { type: "button", value: LocalizedString.Save, style: isMobileInterface ? "margin: 0 0 0 50px;" : "" });
            saveButton.addEventListener("click", this.saveSets);
            const addCurrentButton = addElement("input", container, { type: "button", value: LocalizedString.AddCurrent });
            addCurrentButton.addEventListener("click", function() { armySetsPreferences.drawSetRow(); });
        }
    },
    drawTableHeader: function () {
        if(!this.setsTable) {
            return;
        }
        let cellStyle = "border: 1px solid #f5c137; overflow: hidden;";
        let units = windowObject.obj;
        let tr = addElement("tr", this.setsTable);
        addElement("td", tr, { innerHTML: LocalizedString.SetName.replace(/ /g, "<br>").replace(/-/g, "-<br>"), style: "font-weight: bold; font-size: 8pt; text-align: center; border: 1px solid #f5c137; overflow: hidden;" }); // ; overflow: hidden
        for(let i = 1; i <= 7; i++) {
            addElement("td", tr, { innerHTML: units[i]['name'].replace(/ /g, "<br>").replace(/-/g, "-<br>"), style: "font-weight: bold; font-size: 8pt; text-align: center; border: 1px solid #f5c137; overflow: hidden;", onClick: `ChangeSlider(event, ${i}, 0);` });
        }
        addElement("td", tr, { style: cellStyle });
        addElement("td", tr, { style: cellStyle });
    },
    drawSetRow: function(armySet) {
        const isNew = armySet ? false : true;
        const units = windowObject.obj;
        //console.log(`armySet: ${armySet}, units: ${units}`);
        //console.log(units.slice(1));
        armySet = armySet || { Id: (new Date()).getTime(), Name: "", Army: [units[1], units[2], units[3], units[4], units[5], units[6], units[7]].map(x => x.nownumberd) };
        let cellStyle = "border: 1px solid #f5c137; overflow: hidden;";
        if(!this.setsTable) {
            return;
        }
        let tr = addElement("tr", this.setsTable, { setId: armySet.Id });
        let td = addElement("td", tr, { style: cellStyle });
        let input = addElement("input", td, { value: armySet.Name, onfocus: `this.select();`, style: "width: 97px;" }); //, style: "overflow: hidden;"
        for(let i = 0; i < armySet.Army.length; i++) {
            td = addElement("td", tr, { style: cellStyle });
            input = addElement("input", td, { value: armySet.Army[i], onfocus: `ChangeSlider(event, ${i + 1}, 0); this.select();`, type: "number", style: "min-width: 47px; width: 98%; text-align: right;" });
        }
        td = addElement("td", tr, { style: cellStyle });
        let delButton = addElement("input", td, { type: "button", value: "x", title: LocalizedString.Delete });
        delButton.addEventListener("click", this.deleteSet);

        td = addElement("td", tr, { style: cellStyle });
        if(!isNew) {
            let applyButton = addElement("input", td, { type : "button", value : "v", title : LocalizedString.Apply });
            applyButton.addEventListener("click", function() { armySetsPreferences.saveSets(); applySet(null, armySetsPreferences, { number: parseInt(armySet.Id), name: armySet.Name, title: armySet.Army.join("+"), method: "POST", url: "army_apply.php", data: armySet.Army.reduce((x, y, i) => `${x}&countv${i + 1}=${y}`, ""), contentType: "application/x-www-form-urlencoded" }); });
        }
    },
    saveSets: function () {
        const rows = Array.from(armySetsPreferences.setsTable.rows).slice(1);
        const armySetsData = rows.reduce((t, x) => ({ ...t, [x.getAttribute("setId")]: Array.from(x.cells).slice(0, 8).map(y => y.firstChild.value).join("|") }), {});
        GM_setValue(`ArmySets${PlayerId}Fraction${Fraction}`, JSON.stringify(armySetsData));
    },
    deleteSet: function () {
        const table = this.parentNode.parentNode.parentNode;
        const row = this.parentNode.parentNode;
        table.removeChild(row);
    }
};
/************************************************************************************************************/
const factionsPreferences = {
    menuTitle: LocalizedString.Castle,
    setReferencePage: "castle.php",
    sets: new Array(),
    menuItems: {},
    currentMenuItem: undefined,
    refreshingPages: "home.php;army.php;pl_info.php;castle.php;inventory.php;skillwheel.php",
    initSetsApplyAction: function() {
        const fractions = JSON.parse(GM_getValue(`Fractions${PlayerId}`, "{}"));
        this.sets.length = 0;
        for(const fractionName of Object.keys(fractions)) {
            const set = { number: fractions[fractionName], name: fractionName, method: "GET" };
            const sign = GM_getValue(`Sign${PlayerId}`);
            if(sign) {
                set.url = `castle.php?change_clr_to=${fractions[fractionName]}&sign=${sign}`;
            }
            this.sets.push(set);
        }
    },
    getCurrentSetName: function() { return `Fraction${PlayerId}`; },
    onPageLoad: async function () {
        await this.initCastlesList();
        this.findSetChangersAndAddListener();
    },
    initCastlesList: async function () {
        let doc = document;
        if(!/castle.php/.test(location.href)) {
            if(GM_getValue(`Sign${PlayerId}`)) {
                return;
            }
            doc = await getRequest("/castle.php");
        }
        const castleNamesDivs = doc.querySelectorAll("div.castle_faction_div_inside");
        const fractions = {};
        for(const castleNameDiv of castleNamesDivs) {
            const castleName = castleNameDiv.getAttribute("hint");
            fractions[castleName] = getUrlParamValue(castleNameDiv.firstChild.href, "show_castle_f");
        }
        GM_setValue(`Fractions${PlayerId}`, JSON.stringify(fractions));
        const castle_yes_no_dialogDiv = doc.querySelector("div.castle_yes_no_dialog");
        if(castle_yes_no_dialogDiv) {
            const changeCastleRef = castle_yes_no_dialogDiv.querySelector("a[href*='castle.php?change_clr_to']");
            if(changeCastleRef) {
                GM_setValue(`Sign${PlayerId}`, getUrlParamValue(changeCastleRef.href, "sign"));
            }
        }
    },
    findSetChangersAndAddListener: function() {
        const setRefs = document.querySelectorAll("a[href*='castle.php?change_clr_to']");
        for(const setRef of setRefs) {
            addSetChangerListener(setRef, this, getUrlParamValue(setRef.href, "change_clr_to"));
        }
    },
    setChanged: function(newSetNumber) {
        Fraction = newSetNumber;
        GM_setValue(`Fraction${PlayerId}`, Fraction);
        createMenu();
    }
};

/************************************************************************************************************/
const preferences = [weaponSetsPreferences, skillSetsPreferences, armySetsPreferences, factionsPreferences];
const isHeartOnPage = document.querySelector("canvas#heart") || document.querySelector("div#heart_js_mobile");
main();
function mobileCheck() {
  let check = false;
  (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  return check;
};
function main() {
    getPerksToHome();
    if(isHeartOnPage && PlayerId && Fraction) {
        for(const preference of preferences) {
            if(preference.onPageLoad) {
                preference.onPageLoad();
            }
        }
        createMenu();
        window.addEventListener("resize", function() { createMenu(true); });
        drowSkillChangers();
        const container = document.querySelector("div#home_css_stats_wrap_div");
        observe(container, drowSkillChangers);
    }
}
function getPerksToHome() {
    if(location.pathname == '/home.php') {
        let hwmSetsMasterPerksContainer = document.getElementById("hwmSetsMasterPerksContainer");
        if(!hwmSetsMasterPerksContainer) {
            let homePagePerksContainer;
            if(isNewPersonPage) {
                homePagePerksContainer = document.querySelector("div.home_friends_block");
            } else {
                homePagePerksContainer = document.querySelector("body > center > table:nth-child(2) > tbody > tr:nth-child(1) > td:nth-child(1) > table > tbody > tr:nth-child(2) > td:nth-child(1) > table > tbody > tr > td > table > tbody > tr > td");
            }
            addElement("br", homePagePerksContainer);
            const showPerksTitleSpan = addElement("span", homePagePerksContainer, { innerHTML: `&nbsp;&raquo;&nbsp;<b>${isEn ? "Perks" : "Навыки"}:`, title: getShowPerksTitleSpanTitle() });
            showPerksTitleSpan.addEventListener("click", function() { GM_setValue("ShowPerks", !gmGetBool("ShowPerks")); this.title = getShowPerksTitleSpanTitle(); }, false);
            addElement("br", homePagePerksContainer);
            hwmSetsMasterPerksContainer = addElement("div", homePagePerksContainer, { id: "hwmSetsMasterPerksContainer" });
        }
        getPerksToHomeCore();
    }
}
async function getPerksToHomeCore() {
    if(location.pathname == '/home.php' && gmGetBool("ShowPerks")) {
        const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);
        const perksTable = getParent(doc.querySelector("a[href^='showperkinfo.php']"), "table", 2);
        if(perksTable) {
            Array.from(perksTable.querySelectorAll("a[href^='showperkinfo.php'] > img")).forEach(x => { x.setAttribute("title", x.getAttribute("hint")); x.style.width = `48px`; x.style.height = "auto"; });
            document.getElementById("hwmSetsMasterPerksContainer").innerHTML = perksTable.outerHTML;
        }
    }
}
function getShowPerksTitleSpanTitle() { return isEn ? `Click for ${gmGetBool("ShowPerks") ? "disable" : "enable"} perks loading` : `Нажмите для ${gmGetBool("ShowPerks") ? "выключения" : "включения"} загрузки навыков`; }
function getMenuAnchorElement() {
    // const homeRefs = document.querySelectorAll("a[href='home.php']");
    // console.log(`homeRefs: ${homeRefs.length}`);
    const menuPanel = document.querySelector("div.sh_MenuPanel") || document.querySelector("#main_top_table") || document;
    if(menuPanel) {
        const homeRef = menuPanel.querySelector("a[href='home.php']");
        if(isNewInterface) {
            //homeRef.parentNode.addEventListener("resize", function() { createMenu(true); });
            return homeRef.parentNode;
        } else {
            const result = homeRef.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode;
            if(!result) {
                result = homeRef.parentNode;
            }
            return result;
        }
    }
}
function getTextWidth(text, font) {
    const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas")); // re-use canvas object for better performance
    const context = canvas.getContext("2d");
    context.font = font;
    const metrics = context.measureText(text);
    return metrics.width;
}
function getCssStyle(element, prop) { return window.getComputedStyle(element, null).getPropertyValue(prop); }
function getCanvasFont(el = document.body) {
    const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
    const fontSize = getCssStyle(el, 'font-size') || '16px';
    const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
    return `${fontWeight} ${fontSize} ${fontFamily}`;
}
function getOrCreateAndResizeDropdown(baseElement, style) {
    const dropdownId = `${baseElement.id}Dropdown`;
    let dropdown = document.getElementById(dropdownId);
    if(!dropdown) {
        dropdown = addElement("div", document.body, { id: dropdownId, style: `position: absolute; z-index: ${baseElement.style.zIndex};` + (style || "") });
        if(isMobileDevice) {
            baseElement.addEventListener("click", function() {
                dropdown.style.display = dropdown.style.display == "none" ? "block" : "none";
                if(dropdown.style.display == "block") {
                    const dropdowns = document.querySelectorAll("div[id$=Dropdown]");
                    for(const dropdown of dropdowns) {
                        if(dropdown.id != dropdownId) {
                            dropdown.style.display = "none";
                        }
                    }
                }
            });
        } else {
            let hideTimer;
            const dropdownTimeout = 100;
            baseElement.addEventListener("mouseenter", function() { clearTimeout(hideTimer); if(dropdown.style.display == "none") { setTimeout(function() { dropdown.style.display = "block"; }, dropdownTimeout); } });
            baseElement.addEventListener("mouseleave", function() { hideTimer = setTimeout(function() { dropdown.style.display = "none"; }, dropdownTimeout); });
            dropdown.addEventListener("mouseenter", function() { clearTimeout(hideTimer); });
            dropdown.addEventListener("mouseleave", function() { hideTimer = setTimeout(function() { dropdown.style.display = "none"; }, dropdownTimeout); });
        }
    }
    const baseElementRect = baseElement.getBoundingClientRect();
    dropdown.style.top = `${baseElementRect.bottom + window.scrollY + 1}px`;
    dropdown.style.left = `${baseElementRect.left}px`;
    return dropdown;
}
function createMenu(isResize) {
    const menuAnchor = getMenuAnchorElement();
    if(!menuAnchor) {
        return;
    }
    const anchorRect = menuAnchor.getBoundingClientRect();
    //console.log(anchorRect);
    const borderWidth = isNewInterface ? 1 : 2;
    const menuItemHeight = anchorRect.height - borderWidth * 2;
    const menuItemLineHeight = menuItemHeight - borderWidth;
    const menuItemTop = anchorRect.top + window.scrollY;
    const foreColor = "#f5c137";
    const backgroundColor = isNewInterface ? "linear-gradient(to top, #09203f 0%, #537895 100%)" : (document.querySelector("img[src*='i/top_ny']") ? "#003399" : "#6b6b69");
    const zIndex = location.pathname == "/photo_pl_photos.php" ? "0" : "100";
    let currentMenuItemLeft = anchorRect.left - borderWidth - 1;
    for(let i = preferences.length - 1; i >= 0; i--) {
        const currentPreferences = preferences[i];
        const mainMenuItemId = `SetsMenuItem${i}`;
        let mainMenuItem = document.getElementById(mainMenuItemId);
        if(!mainMenuItem) {
            const menuHeaderStyle = `font-size: 9pt; position: absolute; border-radius: 5px; background: ${backgroundColor}; color: ${foreColor}; border: ${borderWidth}px solid ${foreColor}; padding: 0 3px 0 3px; font-weight: bold; text-align: center; z-index: ${zIndex};`;
            mainMenuItem = addElement("div", document.body, { id: mainMenuItemId, style: menuHeaderStyle });
            if(isMobileDevice) {
                addElement("span", mainMenuItem, { innerHTML: currentPreferences.menuTitle, style: `color: ${foreColor}; text-decoration: none;` });
            } else {
                addElement("a", mainMenuItem, { innerHTML: currentPreferences.menuTitle, href: currentPreferences.setReferencePage, style: `color: ${foreColor}; text-decoration: none;` });
            }
        }
        mainMenuItem.style.height = `${menuItemHeight}px`;
        mainMenuItem.style.lineHeight = `${menuItemLineHeight}px`;
        mainMenuItem.style.top = `${menuItemTop}px`;

        const mainMenuItemRect = mainMenuItem.getBoundingClientRect();
        currentMenuItemLeft -= mainMenuItemRect.width;
        mainMenuItem.style.left = `${currentMenuItemLeft}px`;

        const menuContent = getOrCreateAndResizeDropdown(mainMenuItem, `list-style-position: inside; color: ${foreColor}; padding: 2px 3px 2px 3px; white-space: nowrap; background: ${backgroundColor};`);
        if(isResize) {
            continue;
        }
        currentPreferences.initSetsApplyAction();
        menuContent.style.display = "block"; // Перед заполнением покажем див для правильного определения его размеров (нужно, если он установлен в none)
        menuContent.innerHTML = '';
        const currentSetNumber = GM_getValue(currentPreferences.getCurrentSetName(), -1);
        let maxClientWidth = menuContent.clientWidth;
        for(const currentSet of currentPreferences.sets) {
            const dropDownMenuItem = addElement("li", menuContent, { type: "disc", style: "text-align: left;" });
            const currentSetReference = addElement("b", dropDownMenuItem, { innerHTML: currentSet.name, title: currentSet.title || "", style: `color: ${foreColor}; cursor: pointer;` });
            if(currentSet.url) {
                currentSetReference.addEventListener("click", function() { applySet(currentSetReference, currentPreferences, currentSet); }, false);
            }
            if(currentSet.number == currentSetNumber) {
                markCurrent(currentSetReference, currentPreferences, currentSet.number);
            }
            currentPreferences.menuItems[currentSet.number] = currentSetReference;

            let currentWidth = getTextWidth(currentSet.name, getCanvasFont(currentSetReference));
            if(maxClientWidth < currentWidth) {
                maxClientWidth = currentWidth;
            }
        }
        menuContent.style.minWidth = `${(maxClientWidth + 25)}px`;
        menuContent.style.display = "none";
    }
}
function markCurrent(selectedMenuItem, currentPreferences, currentSetNumber) {
    GM_setValue(currentPreferences.getCurrentSetName(), currentSetNumber);
    if(selectedMenuItem) {
        selectedMenuItem.style.color = '#0f0';
        if (currentPreferences.currentMenuItem && currentPreferences.currentMenuItem != selectedMenuItem) {
            currentPreferences.currentMenuItem.style.color = "#f5c137";
        }
        currentPreferences.currentMenuItem = selectedMenuItem;
    }
}
function addSetChangerListener(htmlElement, currentPreferences, setNumber) {
    htmlElement.addEventListener("click", function() { markCurrent(currentPreferences.menuItems[setNumber], currentPreferences, setNumber); });
}
function applySet(selectedMenuItem, currentPreferences, currentSet) {
    markCurrent(selectedMenuItem, currentPreferences, currentSet.number);
    const originalText = selectedMenuItem ? selectedMenuItem.innerHTML : "";
    if(selectedMenuItem) {
        selectedMenuItem.innerHTML += " " + getLoadGif();
    }
    const request = new XMLHttpRequest();
    request.open(currentSet.method, currentSet.url, true);
    request.onreadystatechange = async function () {
        if(request.readyState == 2) {
            request.abort();
            if(selectedMenuItem) {
                selectedMenuItem.innerHTML = originalText;
                if(isMobileDevice) {
                    selectedMenuItem.parentNode.parentNode.style.display = "none";
                }
            }
            if(typeof(currentPreferences.setChanged) == "function") {
                currentPreferences.setChanged(currentSet.number);
            }
            if(currentPreferences.refreshingPages) {
                let pages = currentPreferences.refreshingPages.split(';');
                for(const page of pages) {
                    if(location.href.indexOf(page) > -1) {
                        let handled = false;
                        if(currentPreferences.updatePanelHandler) {
                            handled = await currentPreferences.updatePanelHandler();
                        }
                        if(!handled) {
                            window.location.reload();
                        }
                    }
                }
            }
            if(currentPreferences.refreshingConditions && currentPreferences.refreshingConditions()) {
                window.location.reload();
            }
        }
    };
    if(currentSet.contentType) {
        request.setRequestHeader('Content-type', currentSet.contentType);
    }
    //console.log(currentSet.data);
    request.send(currentSet.data);
    return false;
}
function drowSkillChangers() {
    if(location.pathname=='/home.php' && !document.querySelector(`#increaseattackAmountInput`)) {
        const isNewPersonPage = document.querySelector("div#home_css_stats_wrap_div") ? true : false;
        let skillsCount = 0;
        let re = new RegExp(isNewPersonPage ? `>${LocalizedString.AvailablePoints}:\\s(\\d+)<` : `<b>${LocalizedString.AvailablePoints}:</b>\\s(\\d+)`);
        const skillsExec = re.exec(document.body.innerHTML);
        if(skillsExec) {
            skillsCount += parseInt(skillsExec[1]);
        }
        re = new RegExp(isNewPersonPage ? `>${LocalizedString.AvailableTalentPoints}:\\s(\\d+)<` : `<b>${LocalizedString.AvailableTalentPoints}:</b>\\s(\\d+)`);
        const perksSkillsExec = re.exec(document.body.innerHTML);
        if(perksSkillsExec) {
            skillsCount += parseInt(perksSkillsExec[1]);
        }
        //console.log(`skillsCount: ${skillsCount}`);
        if(skillsCount == 0) {
            return;
        }
        const skillValueContainers = [];
        if(isNewPersonPage) {
            const container = document.querySelector("div#home_css_stats_wrap_div");
            const inv_stat_dataDivs = container.querySelectorAll("div.inv_stat_data.home_stat_data.show_hint");
            for(const inv_stat_dataDiv of inv_stat_dataDivs) {
                const increaseButton = inv_stat_dataDiv.querySelector("div.home_button2.btn_hover2");
                if(!increaseButton) {
                    continue;
                }
                const skillValueContainer = inv_stat_dataDiv.querySelector("div.inv_stat_text.home_stat_text");
                skillValueContainers.push(skillValueContainer);
            }
        } else {
            const increaseRefs = document.querySelectorAll("a[href^='home.php?increase=']"); //home.php?increase=defence
            for(const increaseRef of increaseRefs) {
                const sklilIncreaseCell = getParent(increaseRef, "td");
                const skillValueContainer = sklilIncreaseCell.previousSibling;
                skillValueContainers.push(skillValueContainer);
            }
        }
        const skillNames = ["attack", "defence", "power", "knowledge"];
        let i = 0;
        for(const skillValueContainer of skillValueContainers) {
            const skillValue = Number((skillValueContainer.querySelector("b") || skillValueContainer).innerText);
            skillValueContainer.innerHTML = "";
            const skill = skillNames[i];
            const increaseAmountInput = addElement("input", skillValueContainer, { id: `increase${skill}AmountInput`, name: "increaseAmountInput", value: skillValue, type: "number", min: skillValue, max: skillValue + skillsCount, size: 4, onfocus: "this.select();", title: LocalizedString.IncreaseManyPointsTooltip.replace("${minValue}", skillValue + 1).replace("${maxValue}", skillValue + skillsCount) });
            increaseAmountInput.addEventListener("change", function() { const targetValue = Number(increaseAmountInput.value); if(targetValue > skillValue && targetValue <= skillValue + skillsCount) { changeSkill(increaseAmountInput, isNewPersonPage, skill, skillValue, targetValue); } });
            i++;
        }
    }
}
async function changeSkill(increaseAmountInput, isNewPersonPage, skill, currentValue, targetValue) {
    while(currentValue < targetValue) {
        const url = `/home.php?increase=${skill}` + (isNewPersonPage ? `&info=1&js_output=1&rand=${Math.random() * 1000000}` : "");
        const txt = await getRequestText(url, isNewPersonPage ? "text/html; charset=UTF-8" : "text/html; charset=windows-1251");
        currentValue++;
        if(isNewPersonPage) {
            if (txt.substring(0, 7) != 'HCSS_OK') {
                window.location = '/home.php?info';
                return;
            }
            const data = txt.split('@');
            const home_css_stats_wrap_div = document.getElementById('home_css_stats_wrap_div');
            if(data && data[1] && home_css_stats_wrap_div) {
                home_css_stats_wrap_div.innerHTML = data[1];
                if(data.length > 2 && document.getElementById('home_css_mana_count')) {
                    document.getElementById('home_css_mana_count').innerHTML = parseInt(data[2]);
                }
                if(typeof windowObject.hwm_hints_init === 'function') windowObject.hwm_hints_init();
            }
        } else {
            increaseAmountInput.value = currentValue;
        }
    }
    if(!isNewPersonPage) {
        location.reload();
    }
}
function getParent(element, parentType, number = 1) {
    let result = element;
    let foundNumber = 0;
    while(result = result.parentNode) {
        if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
            foundNumber++;
            if(foundNumber == number) {
                return result;
            }
        }
    }
}
function addElement(type, parent, data) {
    let el = createElement(type, data);
    if(parent) {
        parent.appendChild(el);
    }
    return el;
}
function createElement(type, data) {
    let el = document.createElement(type);
    if(data) {
        for(let key in data) {
            if(key == "innerText" || key == "innerHTML") {
                el[key] = data[key];
            } else {
                el.setAttribute(key, data[key]);
            }
        }
    }
    return el;
}
function getLoadGif() {
    return '<img border="0" align="absmiddle" height="11" src="data:image/gif;base64,' +
    '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' +
    'fySDhGYQdDWGQyUhADs=">';
}
function ensureGmMethods() {
    if(!this.GM_getValue) {
        this.GM_getValue = function(key, def) { return localStorage[key] || def; };
        this.GM_setValue = function(key, value) { localStorage[key] = value; };
        this.GM_deleteValue = function(key) { return delete localStorage[key]; };
    }
    if(!this.GM_listValues) {
        this.GM_listValues = function() {
            const values = [];
            for(let key in localStorage) {
                values.push(localStorage[key]);
            }
            return values;
        }
    }
}
function getUserName() {
    let enterButton = document.querySelector("input[src$='" + LocalizedString.EnterJpg + "']") || document.querySelector("input[value$='" + LocalizedString.SignInTitle + "']");
    if(/login.php$/.test(location.href)) {
        enterButton = document.querySelector("input[value='" + LocalizedString.SignInTitle + "']");
    }
    let loginTextBox = document.querySelector("input[name='login']");
    if(enterButton && loginTextBox) {
        enterButton.addEventListener("click", function() { GM_setValue("UserName", loginTextBox.value + "_"); });
    } else {
        if(/home.php/.test(location.href)) {
            const playersInfos = document.querySelectorAll("a.pi");
            for(const playersInfo of playersInfos) {
                if(playersInfo.nextElementSibling && playersInfo.nextElementSibling.tagName == "A" && playersInfo.nextElementSibling.href.indexOf("castle.php?change_faction_dialog") > -1) {
                    GM_setValue("UserName", playersInfo.querySelector("b").innerText + "_");
                }
            }
        }
        userName = GM_getValue("UserName");
        if(userName) {
            userName = encodeURIComponent(userName);
        }
    }
}
function getFraction() {
    let currentFactionNumber;
    if(location.pathname == '/home.php') {
        // for new home page
        let currentFactionIconContainer = document.querySelector("div.home_css_pl_fract.show_hint");
        if(!currentFactionIconContainer) {
            currentFactionIconContainer = document.querySelector("a[href^='castle.php?change_faction_dialog']");
        }
        if(currentFactionIconContainer) {
            const currentFactionIconImg = currentFactionIconContainer.querySelector("img");
            currentFactionNumber = currentFactionIconImg.src.split("i/f/r")[1].split(".png")[0];
        }
    } else if(location.pathname=='/pl_info.php' && getUrlParamValue(location.href, "id") == PlayerId) {
        const fractionImage = document.querySelector("img[src*='i/f/r']");
        const regExp = new RegExp('\\/i\\/f\\/r(\\d+)\\.png');
        const regExpExec = regExp.exec(fractionImage.src);
        if(regExpExec) {
            currentFactionNumber = regExpExec[1];
        }
    } else if(location.pathname=='/castle.php') {
        const selectedFactionImg = document.querySelector("div.castle_faction_div_inside2 img");
        const selectedFactionImgName = selectedFactionImg.getAttribute("src");
        const selectedFactionNumber = selectedFactionImgName.split("kukla_png/kukla")[1].split(".")[0]; //dcdn.heroeswm.ru/i/kukla_png/kukla5.png

        const faction_listDiv = document.querySelector("div[id='faction_list']");
        if(faction_listDiv.getAttribute("style").includes("display:none;")) {
            currentFactionNumber = selectedFactionNumber;
        }
    }
    if(currentFactionNumber) {
        GM_setValue(`Fraction${PlayerId}`, currentFactionNumber);
    }
    Fraction = parseInt(GM_getValue(`Fraction${PlayerId}`));
}
function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }
function uchar(s) { switch (s[0]) {case "А": return "\u0410"; case "Б": return "\u0411"; case "В": return "\u0412"; case "Г": return "\u0413"; case "Д": return "\u0414"; case "Е": return "\u0415"; case "Ж": return "\u0416"; case "З": return "\u0417"; case "И": return "\u0418"; case "Й": return "\u0419"; case "К": return "\u041a"; case "Л": return "\u041b"; case "М": return "\u041c"; case "Н": return "\u041d"; case "О": return "\u041e"; case "П": return "\u041f"; case "Р": return "\u0420"; case "С": return "\u0421"; case "Т": return "\u0422"; case "У": return "\u0423"; case "Ф": return "\u0424"; case "Х": return "\u0425"; case "Ц": return "\u0426"; case "Ч": return "\u0427"; case "Ш": return "\u0428"; case "Щ": return "\u0429"; case "Ъ": return "\u042a"; case "Ы": return "\u042b"; case "Ь": return "\u042c"; case "Э": return "\u042d"; case "Ю": return "\u042e"; case "Я": return "\u042f"; case "а": return "\u0430"; case "б": return "\u0431"; case "в": return "\u0432"; case "г": return "\u0433"; case "д": return "\u0434"; case "е": return "\u0435"; case "ж": return "\u0436"; case "з": return "\u0437"; case "и": return "\u0438"; case "й": return "\u0439"; case "к": return "\u043a"; case "л": return "\u043b"; case "м": return "\u043c"; case "н": return "\u043d"; case "о": return "\u043e"; case "п": return "\u043f"; case "р": return "\u0440"; case "с": return "\u0441"; case "т": return "\u0442"; case "у": return "\u0443"; case "ф": return "\u0444"; case "х": return "\u0445"; case "ц": return "\u0446"; case "ч": return "\u0447"; case "ш": return "\u0448"; case "щ": return "\u0449"; case "ъ": return "\u044a"; case "ы": return "\u044b"; case "ь": return "\u044c"; case "э": return "\u044d"; case "ю": return "\u044e"; case "я": return "\u044f"; case "Ё": return "\u0401"; case "ё": return "\u0451"; default: return s[0]; } }
function ustring(s) {
    s = String(s);
    let result = "";
    for(let i = 0; i < s.length; i++) {
        result += uchar(s[i]);
    }
    return result;
}
function getRequest(url, overrideMimeType) {
    return new Promise((resolve, reject) => {
        GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: overrideMimeType || "text/html; charset=windows-1251",
            onload: function(response) { resolve((new DOMParser).parseFromString(response.responseText, "text/html")); },
            onerror: function(error) { reject(error); }
        });
    });
}
function getRequestText(url, overrideMimeType) {
    return new Promise((resolve, reject) => {
        GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: overrideMimeType || "text/html; charset=windows-1251",
            onload: function(response) { resolve(response.responseText); },
            onerror: function(error) { reject(error); }
        });
    });
}
function postRequest(url, data) {
    return new Promise((resolve, reject) => {
        GM.xmlHttpRequest({ method: "POST", url: url, headers: { "Content-Type": "application/x-www-form-urlencoded" }, data: data,
            onload: function(response) { resolve(response); },
            onerror: function(error) { reject(error); }
        });
    });
}
function observe(target, handler, config = { childList: true, subtree: true }) {
    const ob = new MutationObserver(async function(mut, observer) {
        //console.log(`Mutation start`);
        observer.disconnect();
        if(handler.constructor.name === 'AsyncFunction') {
            await handler();
        } else {
            handler();
        }
        observer.observe(target, config);
    });
    ob.observe(target, config);
}
function gmGetBool(valueName, defaultValue = false) {
    const value = GM_getValue(valueName);
    if(value) {
        if(typeof(value) == "string") {
            return value == "true";
        }
        if(typeof(value) == "boolean") {
            return value;
        }
    }
    return defaultValue;
}