hwmWidget

Виджет для главной страницы ГВД

当前为 2023-11-27 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        hwmWidget
// @include     /^https{0,1}:\/\/((www|qrator)\.heroeswm\.ru|178\.248\.235\.15)\/home\.php/
// @description Виджет для главной страницы ГВД
// @version     4.6
// @author      Tamozhnya1
// @namespace   Tamozhnya1
// @grant       GM.xmlHttpRequest
// @grant       unsafeWindow
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addStyle
// @license     MIT
// ==/UserScript==
if(typeof GM_getValue != 'function') {
    this.GM_getValue = function (key,def) { return localStorage[key] || def; };
    this.GM_setValue = function (key,value) { return localStorage[key] = value; };
    this.GM_deleteValue = function (key) { return delete localStorage[key]; };
}
const LotType = { Purchase: 1, Auction: 2 };
var ElementsTypes = { "42": "abrasive", "43": "snake_poison", "46": "tiger_tusk", "44": "ice_crystal", "45": "moon_stone", "40": "fire_crystal", "37": "meteorit", "41": "witch_flower", "39": "wind_flower", "78": "fern_flower", "38": "badgrib" }
var ElementNames = ["abrasive", "snake_poison", "tiger_tusk", "ice_crystal", "moon_stone", "fire_crystal", "meteorit", "witch_flower", "wind_flower", "fern_flower", "badgrib"];
var ResourcesTypes = { "wood": { Type: "1", ImageName: "wood" }, "ore": { Type: "2", ImageName: "ore" }, "mercury": { Type: "3", ImageName: "mercury" }, "sulphur": { Type: "4", ImageName: "sulfur" }, "crystal": { Type: "5", ImageName: "crystals" }, "gem": { Type: "6", ImageName: "gems" } };
var forumNames = { "2": "ОиФ", "10": "ВиП", "24": "Турниры", "3": "ИиП", "12": "БиП", "11": "ФВТ", "27": "Встречи", "14": "Обычные артефакты", "21": "Аренда", "22": "УКиО", "23": "ПЭСиП", "25": "ПЗ(Бои)", "13": "ПЗ(Финансы)", "7": "ТП", "8": "ОиС" };
const locations = {
    1: [50,50,"Empire Capital","EmC","Столица Империи"],
    2: [51,50,"East River","EsR","Восточная Река"],
    3: [50,49,"Tiger Lake","TgL","Тигриное Озеро"],
    4: [51,49,"Rogues' Wood","RgW","Лес Разбойников"],
    5: [50,51,"Wolf Dale","WoD","Долина Волков"],
    6: [50,48,"Peaceful Camp","PcC","Мирный Лагерь"],
    7: [49,51,"Lizard Lowland","LzL","Равнина Ящеров"],
    8: [49,50,"Green Wood","GrW","Зеленый Лес"],
    9: [49,48,"Eagle Nest","EgN","Орлиное Гнездо"],
    10: [50,52,"Portal Ruins","PoR","Руины Портала"],
    11: [51,51,"Dragons' Caves","DrC","Пещеры Драконов"],
    12: [49,49,"Shining Spring","ShS","Сияющий Родник"],
    13: [48,49,"Sunny City","SnC","Солнечный Город"],
    14: [52,50,"Magma Mines","MgM","Магма Шахты"],
    15: [52,49,"Bear Mountain","BrM","Медвежья Гора"],
    16: [52,48,"Fairy Trees","FrT","Магический Лес"],
    17: [53,50,"Harbour City","HrC","Портовый Город"],
    18: [53,49,"Mythril Coast","MfC","Мифриловый Берег"],
    19: [51,52,"Great Wall","GtW","Великая Стена"],
    20: [51,53,"Titans' Valley","TiV","Равнина Титанов"],
    21: [52,53,"Fishing Village","FsV","Рыбачье село"],
    22: [52,54,"Kingdom Castle","KiC","Замок Королевства"],
    23: [48,48,"Ungovernable Steppe","UnS","Непокорная Степь"],
    24: [51,48,"Crystal Garden","CrG","Кристальный Сад"],
    25: [53,52,"East Island","EsI","Восточный Остров"],
    26: [49,52,"The Wilderness","ThW","Дикие земли"],
    27: [48,50,"Sublime Arbor","SbA","Великое Древо"]
};
const isEn = document.documentElement.lang == "en";
const Strings = { "ru": { BuyNow: "Купить сразу!" }, "en": { BuyNow: "Buy now!" } };
const LocalizedString = Strings[document.documentElement.lang];
GM_addStyle(`
.hover-link:hover{ color: red }
.news-head {
  text-decoration:none;
  align-self: center;
  border-radius: 1.5rem;
  padding: 0.25rem .75rem;
}

.active {
  background: #eae8dd;
}

.active:hover {
  background: #eae8dd80;
}

.news-head__title {
  display: inline;
  font-size: 12px;
  font-weight: normal;
  cursor: pointer;
}

.news-head__switch {
  cursor: pointer;
  align-self: center;
  color: #5D413A40;
  margin-left:10px;
}

.news-head__settings {
  cursor: pointer;
  align-self: center;
  color: #5D413A40;
  margin-left:10px;
  width: 1.5%;
}

.mrgn-l{
  margin-left: 5px;
}

.flex {
  display: flex;
}

.div-style {
  margin: 0 auto 10px;
  padding: 15px 25px 20px;
  overflow: hidden;
  min-width: 400px;
  border-radius: 5px;
  border: 0 #adadad solid;
  background: url(../i/inv_im/corner_lt2.png) no-repeat top left, url(../i/inv_im/corner_rt2.png) no-repeat top right, url(../i/inv_im/corner_lb2.png) no-repeat bottom left, url(../i/inv_im/corner_rb2.png) no-repeat bottom right #f5f3ea;
  background-size: 14px;
  box-shadow: inset 0 0 0 1px #b19673, 0 2px 5px rgb(0 0 0 / 25%);
}

.res-style {
  display: none;
  justify-content: space-around;
  height: 40px;
  background-color: #eae8dd;
  border-radius: 5px;
  border: 0 #adadad solid;
  margin-top: 10px;
}

.modal {
  width: 100%;
  height: 100%;
  z-index: 1000;
  position: fixed;
  top:0;
  left:0;
  background: #00000050;
  display: none;
  align-items: center;
}

.modal-block {
  display: flex;
  width: 800px;
  height: 500px;
  z-index: 120;
  margin: 0 auto;
  position: relative;
  border-radius: 5px;
  border: 0 #adadad solid;
  background: url(../i/inv_im/corner_lt2.png) no-repeat top left, url(../i/inv_im/corner_rt2.png) no-repeat top right, url(../i/inv_im/corner_lb2.png) no-repeat bottom left, url(../i/inv_im/corner_rb2.png) no-repeat bottom right #f5f3ea;
  background-size: 14px;
  box-shadow: inset 0 0 0 1px #b19673, 0 2px 5px rgb(0 0 0 / 25%);
  padding: 15px;
  align-items: flex-start;
  flex-direction: column;
  overflow: scroll;
}

.modal-block__head {
  display: flex;
  align-items: center;
}

.modal-block__text {
  margin-top: 20px;
  color: #6e6e6e;
}

.modal-block__btn {
  padding: 5px 15px;
  white-space: nowrap;
  position: relative;
  text-align: center;
  color: #592C08;
  background: url(../i/shop_images/art_btn_bg_gold.png) #DAB761;
  background-size: 100% 100%;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border: 0 solid;
  border-radius: 5px;
  box-shadow: inset 0 0 0 1px #fce6b0, inset 0 0 0 2px #a78750, 0 0 0 1px rgb(0 0 0 / 13%);
  cursor: pointer;
  margin-left: 150px;
}

.modal-block__setting {
  display: flex;
  flex-wrap: wrap;
}

.modal-block__checkbox {
  display: flex;
  align-items: center;
  margin: 0 8px 8px 5px;
  padding: 0;
}

.clan-block {
  flex-direction: column;
}

.clan-style {
  display: inline-flex;
  background-color: #adadad40;
  padding: 3px 7px;
  margin-left: 7px;
  border: 0;
  border-radius: 4px;
  color: #592C08;
}

.res-style__elem {
  align-self: center;
  display: flex;
}

.text-title {
  text-align:left;
  padding-top: 6px;
}

@media screen and (min-width: 320px) and (max-width: 600px) {
  .div-style {
    width: auto;
  }
  
  .news-head__title {
    font-size: 10px;
  }
  
  .res-style {
    flex-wrap: wrap;
    height: auto;
    padding: 5px;
  }
  .res-style__elem {
    margin: 0 10px 10px 0;
    font-size: 12px;
  }
  .text-title {
    font-size: 12px;
  }
  .news-head__settings {
    width: 10%;
  }
  .modal-block {
    width: 500px;
    height: 800px;
  }
}`);
const isNewPersonPage = document.querySelector("div#hwm_no_zoom") ? true : false;
const isMobileInterface = document.querySelector("div#btnMenuGlobal") ? true : false;

main();
async function main() {
    //const widgetContainer = isNewPersonPage ? document.querySelector("center > div") : document.querySelector("body > center > table:nth-child(2)");
    let widgetContainer = isNewPersonPage ? document.querySelector("body > center") : document.querySelector("body > center > table:nth-child(2) > tbody > tr > td > table > tbody > tr:nth-child(6) > td:nth-child(1)");
    if(isMobileInterface) {
        widgetContainer = isNewPersonPage ? document.querySelector("div#set_mobile_max_width") : document.querySelector("div#android_container > table > tbody > tr:nth-child(6) > td:nth-child(1)");
        if(isNewPersonPage) {
            widgetContainer.style.flexWrap = "wrap";
            addElement("div", widgetContainer, { style: "flex-basis: 100%; height: 0;"});
        }
    }
    if(widgetContainer) {
        let divOuterInnerHTML = `
<div class="flex">
  <div id="widget" class="flex">
       <div id="prevDaily" class="news-head active">
           <span>📰</span>
           <h2 id="prevDaily_t" class="news-head__title" title="Новости HWM Daily">Новости HWM Daily</h2>
       </div>
       <h2 id="prevForum" class="news-head news-head__title mrgn-l" title="Последние темы форума">Последние темы форума</h2>
       <h2 id="prevClan" class="news-head news-head__title mrgn-l" title="Клановая рассылка">Клановая рассылка</h2>
  </div>
  <span id="switcher" class="news-head__switch"></span>
  <span id="hwm_settings" class="news-head__settings" title="Настройки"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="cog" class="svg-inline--fa fa-cog fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"></path></svg></span>
</div>
<div class="modal" id="modal">
  <div class="modal-block">
      <div class="modal-block__head">
        <h3 class="modal-block__title">Настройки виджета</h3>
        <button id="modal-close" class="modal-block__btn">Закрыть</button>
      </div>
      <form>
        <p class="modal-block__text">Выбор форума</p>
        <div class="modal-block__setting">`;
        for(const forumId of Object.keys(forumNames)) {
            divOuterInnerHTML += `<div class="modal-block__checkbox"><input type="radio" id="${forumId}" name="forum" ${GM_getValue("ForumId", "2") == forumId ? "checked" : ""}><label for="${forumId}">${forumNames[forumId]}</label></div>`;
        }
        divOuterInnerHTML += `</div>
      </form>
      <form>
        <p class="modal-block__text">Выбор клана</p>
        <div class="modal-block__setting clans-block">
        </div>
      </form>
      <label for="ShowItemsAmountInput">Количество отображаемых сообщений</label><input type="number" id="ShowItemsAmountInput" value="${GM_getValue("ShowItemsAmount", 5)}" onfocus="this.select();"/>
  </div>
</div>`;
        const divOuter = addElement('div', widgetContainer, { class: "div-style", innerHTML: divOuterInnerHTML }, GM_getValue("top", false));
        fillClansList();
        addElement('div', divOuter, { id: "NewsPanel" });
        addElement('div', divOuter, { id: "ResourcesPanel", class: "res-style" });
        if(isNewPersonPage) {
            const anchorRect = document.querySelector("div#set_mobile_max_width").getBoundingClientRect();
            //console.log(anchorRect.width)
            divOuter.style.width = `${anchorRect.width}px`;
        }
        const switcher = document.getElementById('switcher');
        switcher.addEventListener("click", function(event) { GM_setValue("Shown", 1 - Number(GM_getValue("Shown", 1))); show(); }, false);
     
        const hwmSettings = document.getElementById('hwm_settings'); 
        hwmSettings.addEventListener("click", function(event) {
            document.querySelector("#modal").style.display = 'flex';
            document.querySelector("html").style.overflowY = 'hidden';
        }, false);
      
        const prevDaily = document.getElementById('prevDaily');
        prevDaily.addEventListener("click", function(event) { GM_setValue("SecondClick", parseInt(GM_getValue("SelectedTab", 0)) == 0); GM_setValue("SelectedTab", 0); show(); }, false);
        const prevForum = document.getElementById('prevForum');
        prevForum.addEventListener("click", function(event) { GM_setValue("SecondClick", parseInt(GM_getValue("SelectedTab", 0)) == 1); GM_setValue("SelectedTab", 1); show(); }, false);
        const prevClan = document.getElementById('prevClan');
        prevClan.addEventListener("click", function(event) { GM_setValue("SecondClick", parseInt(GM_getValue("SelectedTab", 0)) == 2); GM_setValue("SelectedTab", 2); show(); }, false);

        const closeSettings = document.getElementById('modal-close');
        closeSettings.addEventListener("click", function(event) {
            document.querySelector("#modal").style.display = 'none';
            document.querySelector("html").style.overflowY = 'overlay';
            GM_setValue("ForumId", document.querySelector(`input[name='forum']:checked`).id);
            const selectedClan = document.querySelector(".clans-block input[name='clan']:checked");
            if(selectedClan) {
                GM_setValue("ClanId", selectedClan.id);
            } else {
                GM_deleteValue("ClanId");
            }
            GM_setValue("ShowItemsAmount", document.getElementById("ShowItemsAmountInput").value);
            show();
        }, false);
        
        GM_setValue("SecondClick", false);
        show();
    }
}
async function fillClansList() {
    const doc = await getRequest(`https://www.heroeswm.ru/pl_clans.php`);
    const clanInfos = Array.from(doc.querySelectorAll("td > li > a[href^='clan_info.php']")).map(x => { return { Id: getUrlParamValue(x.href, "id"), Name: x.firstChild.innerText }; });
    if(clanInfos.length > 0) {
        if(!GM_getValue("ClanId") || !clanInfos.find(x => x.Id == GM_getValue("ClanId"))) {
            GM_setValue("ClanId", clanInfos[0].Id);
        }
        let clans = '';
        for(const clanInfo of clanInfos) {
            clans += `
<div class="modal-block__checkbox">
<input type="radio" id="${clanInfo.Id}" name="clan" ${clanInfo.Id == GM_getValue("ClanId") ? "checked" : ""}>
<label for="${clanInfo.Id}">#${clanInfo.Id} ${clanInfo.Name}</label>
</div>`;
        }
        document.querySelector(".clans-block").innerHTML = clans;
    } else {
        GM_deleteValue("ClanId");
        document.querySelector(".clans-block").innerHTML = "Вы не состоите в кланах";
    }
}
async function show() {
    //console.log(`Shown: ${GM_getValue("Shown")}, SelectedTab: ${GM_getValue("SelectedTab")}, SecondClick: ${GM_getValue("SecondClick")}`);
    if(gmGetBool("SecondClick")) {
        switch(parseInt(GM_getValue("SelectedTab", 0))) {
            case 0: window.open("https://daily.heroeswm.ru/", "_blank"); break;
            case 1: window.open(`https://www.heroeswm.ru/forum_thread.php?id=${GM_getValue("ForumId", "2")}`, "_blank"); break;
            case 2: window.open(`https://www.heroeswm.ru/sms_clans.php?clan_id=${GM_getValue("ClanId")}`, "_blank"); break;
        }
        return;
    }
    const shown = GM_getValue("Shown", 1);
    const switcher = document.getElementById('switcher');
    const newsPanel = document.getElementById("NewsPanel");
    const resourcesPanel = document.getElementById("ResourcesPanel");
    if(shown == 0) {
        switcher.innerHTML = '<img src="https://dcdn3.heroeswm.ru/i/inv_im/btn_expand.svg" style="-webkit-transform: rotate(90deg); transform: rotate(90deg);">';
        // <img src="https://dcdn3.heroeswm.ru/i/inv_im/btn_expand.svg" class="home_scroll_content_expand_sign inv_rotate90">
        newsPanel.style.display = "none";
        resourcesPanel.style.display = "none";
    } else {
        const prevDaily = document.getElementById('prevDaily');
        const prevForum = document.getElementById('prevForum');
        const prevClan = document.getElementById('prevClan');
        prevDaily.style.background = "#eae8dd00";
        prevForum.style.background = "#eae8dd00";
        prevClan.style.background = "#eae8dd00";
        switch(parseInt(GM_getValue("SelectedTab", 0))) {
            case 0: prevDaily.style.background = "#eae8dd"; getDailyNews(); break;
            case 1: prevForum.style.background = "#eae8dd"; getForumNews(); break;
            case 2: prevClan.style.background = "#eae8dd"; getClanNews(); break;
        }
        switcher.innerHTML = '<img src="https://dcdn3.heroeswm.ru/i/inv_im/btn_expand.svg" style="-webkit-transform: rotate(270deg); transform: rotate(270deg);">';
        newsPanel.style.display = "block";
        await getResources();
        resourcesPanel.style.display = 'flex'
    }
}
function trimming(string, l) {
    //console.log(`string: ${string}, l: ${l}`);
    var s = string;
    if(string.length > l) {
        for(var i = l; i >= 0; i--) {
            if(string.charAt(i) == ' ') {
                s = string.substr(0, i) + '...';
            }
        }
        s = string.substr(0, l) + '...';
    }
    return s.replace(/&[^#]/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;");
}
async function getElementLots() {
    const doc = await getRequest(`https://www.heroeswm.ru/auction.php?cat=elements&sort=0`);
    const firstLotRow = doc.querySelector("center > table:nth-child(2) > tbody > tr > td > table > tbody > tr:nth-child(2) > td:nth-child(2) > table > tbody > tr:nth-child(3)");
    let arts = [];
    let row = firstLotRow;
    while(row = row.nextElementSibling) {
        const art = parseLotRow(row);
        if(art) {
            arts.push(art);
        }
    }
    const grouppedArts = groupBy(arts.filter(x => x.LotType == LotType.Purchase), "ArtId");
    return grouppedArts;
}
function parseLotRow(row) {
    if(!row || row.nodeName != "TR") {
        return;
    }
    const goldImageElement = row.querySelector("img[src*='gold.png']");
    if(!goldImageElement) {
        return;
    }
    const lotPrice = parseFloat(goldImageElement.parentNode.nextElementSibling.innerText.replace(/,/g, ""));
    let artId;
    let lotAmount = 1;
    const lotAmountExec = new RegExp(`(\\d+) ${isEn ? "pcs." : "шт."}`).exec(row.innerHTML);
    //console.log(lotAmountExec);
    if(lotAmountExec) {
        lotAmount = parseInt(lotAmountExec[1]);
    }
    const lotType = row.innerHTML.includes(LocalizedString.BuyNow) ? LotType.Purchase : LotType.Auction;
    const artImageRefElement = row.querySelector("a[href*='art_info.php']");
    if(!artImageRefElement) {
        const elementsList = Object.values(ElementsTypes).join("|");
        const elementParse = (new RegExp(`gn_res/(${elementsList}).png`)).exec(row.innerHTML);
        if(elementParse) {
            artId = elementParse[1];
        }
        if(row.innerHTML.includes("house_cert")) {
            const locationsList = Object.values(locations).map(x => x[2]).join("|");
            const sertParse = (new RegExp(`<br>(${locationsList})&nbsp;<b>`)).exec(row.innerHTML);    
            if(sertParse) {
                artId = getSertIdByLocationName(sertParse[1]);
            }
        }
        const resourcesList = Object.values(ResourcesTypes).map(x => x.ImageName).join("|");
        const resourceParse = (new RegExp(`/(${resourcesList}).png`)).exec(row.innerHTML);
        if(resourceParse) {
            artId = "res_" + resourceParse[1];
        }
        if(row.innerHTML.includes("auc_dom")) {
            const locationsList = Object.values(locations).map(x => x[2]).join("|");
            const sertParse = (new RegExp(`<br>(${locationsList})&nbsp;<b>`)).exec(row.innerHTML);    
            if(sertParse) {
                artId = getHouseIdByLocationName(sertParse[1]);
            }
        }
        if(row.innerHTML.includes("obj_share_pic")) {
            const locationsList = Object.values(locations).map(x => x[2]).join("|");
            const sertParse = (new RegExp(`<br>(${locationsList})&nbsp;<b>`)).exec(row.innerHTML);    
            if(sertParse) {
                artId = getShaIdByLocationName(sertParse[1]);
            }
        }
    } else {
        artId = getUrlParamValue(artImageRefElement.href, "id");
        var artUid = getUrlParamValue(artImageRefElement.href, "uid");;
        const strengthData = row.innerText.match(/\d+\/\d+/);
        var restLotStrength = parseInt(strengthData[0].split("/")[0]);
        var lotStrength = parseInt(strengthData[0].split("/")[1]);
    }
    const lotRef = row.querySelector("a[href^='auction_lot_protocol.php']");
    const lotId = getUrlParamValue(lotRef.href, "id");
    
    const imgR = row.querySelector("td:nth-child(1) > table > tbody > tr > td:nth-child(1) > img");
    
    return { ArtUid: artUid, ArtId: artId, LotStrength: lotStrength, RestLotStrength: restLotStrength, LotPrice: lotPrice, LotType: lotType, LotAmount: lotAmount, LotId: lotId, ImageUrl: imgR.getAttribute('src'), Title: imgR.getAttribute('title') };
}
async function getResources() {
    const resourcesPanel = document.getElementById("ResourcesPanel");
    resourcesPanel.innerHTML = getWheelImage();
    const grouppedArts = await getElementLots();
    const elementsData = [];
    for(const elementName of ElementNames)
    {
        const arts = grouppedArts[elementName]
        if(arts.length == 0) {
            continue;
        }
        const art = arts[0];
        let price = art.LotPrice;
        let secondLotPrice = price;
        if(arts.length > 1) {
            secondLotPrice = arts[1].LotPrice;
        }
        elementsData.push({ ElementName: elementName,
            Price: price,
            ImageUrl: art.ImageUrl,
            Title: art.Title,
            Diffrence: secondLotPrice - price,
            AuctionUrl: `https://www.heroeswm.ru/auction.php?cat=elements&sort=0&art_type=${elementName}`,
            NewAuctionUrl: `https://www.heroeswm.ru/auction_new_lot.php?${elementName}=${(price - 1)}`
        }); 
    }
    //console.log(elementsData);
    let res = "";
    for(const elementData of elementsData)
    {                                    
        res += `
<div class='res-style__elem'>
<div style='align-self: center;'>
    <a class='hover-link' href='${elementData.NewAuctionUrl}' target='_blank'>
        <img src='${elementData.ImageUrl}' width='20' heigth='20' border='0'>
    </a>
</div>
<a class='hover-link' target='_. blank' style='text-decoration: none; align-self: center; margin-left: 5px; font-size: 9px;' href='${elementData.AuctionUrl}' title='Разница первого и второго лотов: ${elementData.Diffrence}'>${elementData.Price}</a>
<div style='${(elementData.Diffrence >= 150 ? 'display: inline-flex; background-color: #f33800; padding: 5px;margin-left: 5px; border: 0; border-radius: 4px; color: #fff;' : 'display: none;')}'>
    <span title='' style='font-size: 8px; font-weight: bold;'>${elementData.Diffrence}</span>
</div>
</div>`;
    }
    resourcesPanel.innerHTML = res;
}
async function getClanNews() {
    if(!GM_getValue("ClanId")) {
        document.getElementById("NewsPanel").innerHTML = "Вы не состоите в кланах";
        return;
    }
    const doc = await getRequest(`https://www.heroeswm.ru/sms_clans.php?clan_id=${GM_getValue("ClanId")}`);
    const letters = Array.from(doc.querySelectorAll("table.wbwhite a[href^='/sms_clans.php'][href*='read']")).map(x => { return {
        Title: x.innerHTML,
        Ref: x.href,
        DateText: x.parentNode.previousElementSibling.innerHTML,
        IsHot:  (Date.now() - parseDate(x.parentNode.previousElementSibling.innerHTML, false, true).getTime()) / (1000 * 60 * 60) <= 12,
    }; }).slice(0, parseInt(GM_getValue("ShowItemsAmount", 5)));
    //console.log(letters);
    //console.log(`letters: ${letters.length}`);
    let clanLetters = "";
    for(const letter of letters) {
        clanLetters += `
<div class='text-title'>
    <a class='hover-link' style='text-decoration:none; ${letter.IsHot ? "font-weight: bold; color: red;" : ""}' target='_blank' href='${letter.Ref}' title='${letter.Title}'>${letter.IsHot ? "📣 " : "• "}${letter.Title}</a>
    <div class='clan-style'>
        <span title='Дата' style='font-size:9px'>${letter.DateText}</span>
    </div>
</div>
`;
    }
    document.getElementById("NewsPanel").innerHTML = clanLetters;
}
async function getForumNews() {
    const forumId = GM_getValue("ForumId", "2");
    const doc = await getRequest(`https://www.heroeswm.ru/forum_thread.php?id=${forumId}`);
    const messages = Array.from(doc.querySelectorAll("tr > td:nth-child(1) > a[href^='forum_messages.php']")).map(x => { return {
        Fixed: getParent(x, "tr").querySelector("img[src*='skrepka.gif']") ? true : false,
        Title: x.innerHTML,
        Reference: x.href,
        LastCommentReference: getParent(x, "tr").querySelector("a[href^='forum_messages.php'][href*='page=last']"),
        CommentsAmount: parseInt(getParent(x, "tr").cells[2].innerHTML),
        IsHot: parseInt(getParent(x, "tr").cells[2].innerHTML) <= 20
    };}).filter(x => !x.Fixed).slice(0, parseInt(GM_getValue("ShowItemsAmount", 5)));
    let forumNews = "";
    for(const message of messages) {
        forumNews += `
<div class='text-title'>
    <a class='hover-link' style='text-decoration: none;${(message.IsHot ? ' font-weight: bold; color: #ff4d00' : '')}' target='_blank' href='${message.Reference}' title='${message.Title}'>${(message.IsHot ? "🔥" : "•")} ${message.Title}
    </a>
    <div style='display: inline-flex; background-color: #adadad40;padding: 3px 7px; margin-left: 7px; border: 0; border-radius: 4px; color: #592C08;'>
        <a href="${message.LastCommentReference}" title='Количество комментариев. Перейти к последнему.' target='_blank' style='font-size: 9px'>${message.CommentsAmount}</a>
    </div>
</div>`;
    }
    document.getElementById("NewsPanel").innerHTML = forumNews;
}
function getDailyNews() {
    const newsPanel = document.getElementById("NewsPanel");
    newsPanel.innerHTML = `${getWheelImage()}&nbsp;&nbsp;Загрузка списка новостей...`;
    GM.xmlHttpRequest({method: "GET", url: "https://daily.heroeswm.ru/news4script.txt?" + Date.now(), headers: {
        'User-agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1)',
        'Accept': 'text/xml,text/html',
        'Content-Type': 'text/plain; charset=windows-1251'
    },
    synchronous: false,
    overrideMimeType: 'text/plain; charset=windows-1251',
    onload: function(response) {
        try {
            const dailyUrlRegExp = /\/\/daily\.heroeswm\.ru\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*$/i;
            const previousMessageIds = GM_getValue("PreviousDailyMessageIds", "").split("|");
            const messages = JSON.parse('[["' + response.responseText.replace(/"/g, "\\\"").replace(/\n/g, '"],["').replace(/;;/g, '","').replace(/'/g, "&#39;") + '"]]').filter(x => x.length == 6).map(x => { return {
                Code: x[0],
                ImageUrl: x[1],
                Title: trimming(x[2], 255),
                Url: x[3],
                Id: x[4],
                CommentsAmount: x[5],
                IsHot: !previousMessageIds.includes(x[4])
            };}).filter(x => dailyUrlRegExp.test(x.ImageUrl) && dailyUrlRegExp.test(x.Url) && /^[1-3]$/.test(x.Code));
            GM_setValue("PreviousDailyMessageIds", messages.map(x => x.Id).join("|"));
            //console.log(messages);
            let newsText = "";
            for(const message of messages) {
                newsText += `
<div class='text-title'>
    <a class='hover-link' style='text-decoration: none;${message.IsHot ? 'font-weight: bold; color:red' : ''}' target='_blank' href='${message.Url}'>${message.IsHot ? "⚡" : "•"} ${message.Title}</a>
    <div style='display: inline-flex; background-color: #adadad40; padding: 3px 7px; margin-left: 7px; border: 0; border-radius: 4px; color: #592C08;'>
        <span title='комментариев' style='font-size:9px'>${message.CommentsAmount}</span>
    </div>
</div>`;
            }
            newsPanel.innerHTML = newsText;
        } catch(e) {
            newsPanel.innerHTML = `Ошибка при обработке данных: ${e}`;
        }
    },
    onerror: function(response) { newsPanel.innerHTML = "Ошибка при получении данных с Daily"; }
    });
}
function GM_addStyle(css) {
    var head = document.getElementsByTagName('head')[0];
    if (!head)
      return;
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}
function addElement(type, parent, data, insertFirst = false) {
    let el = createElement(type, data);
    if(parent) {
        if(insertFirst) {
            parent.insertBefore(el, parent.firstChild);
        } else {
            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 getWheelImage() { return '<img border="0" align="absmiddle" height="11" src="https://dcdn.heroeswm.ru/css/loading.gif">'; }
function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }
function groupBy(list, key) { return list.reduce(function(rv, item) { (rv[item[key]] = rv[item[key]] || []).push(item); return rv; }, {}); };
function getSertIdByLocationNumber(locationNumber) { return "sec_" + (locationNumber.toString()).padStart(2, "0"); }
function getSertIdByLocationName(locationName) { 
    const locationNumber = Object.keys(locations).find(x => locations[x][2] == locationName);
    return getSertIdByLocationNumber(locationNumber);
}
function getHouseIdByLocationNumber(locationNumber) { return "dom_" + (locationNumber.toString()).padStart(2, "0"); }
function getHouseIdByLocationName(locationName) { 
    const locationNumber = Object.keys(locations).find(x => locations[x][2] == locationName);
    return getHouseIdByLocationNumber(locationNumber);
}
function getRequest(url) {
    return new Promise((resolve, reject) => {
        GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: "text/html; charset=windows-1251",
            onload: function(response) { resolve((new DOMParser).parseFromString(response.responseText, "text/html")); },
            onerror: function(error) { reject(error); }
        });
    });
}
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;
}
function parseDate(dateString, isFuture = false, isPast = false) {
    //console.log(dateString)
    if(!dateString) {
        return;
    }
    const dateStrings = dateString.split(" ");
    
    let hours = 0;
    let minutes = 0;
    let seconds = 0;
    const timePart = dateStrings.find(x => x.includes(":"));
    if(timePart) {
        var time = timePart.split(":");
        hours = parseInt(time[0]);
        minutes = parseInt(time[1]);
        if(time.length > 2) {
            seconds = parseInt(time[2]);
        }
    }

    const now = new Date();
    let year = now.getFullYear();
    let month = now.getMonth();
    let day = now.getDate();
    const datePart = dateStrings.find(x => x.includes("-"));
    if(datePart) {
        const date = datePart.split("-");
        month = parseInt(date[isEn ? (date.length == 3 ? 1 : 0) : 1]) - 1;
        day = parseInt(date[isEn ? (date.length == 3 ? 2 : 1) : 0]);
        if(date.length == 3) {
            year = isEn ? parseInt(date[0]) : parseInt(date[2]);
            if(year < 1000) {
                year += Math.floor((new Date()).getFullYear() / 1000) * 1000;
            }
        } else {
            if(isFuture && month == 0 && now.getMonth() == 11) {
                year += 1;
            }
        }
    }
    if(dateStrings.length > 2) {
        const letterDateExec = /(\d{2}):(\d{2}) (\d{2}) (.{3,4})/.exec(dateString);
        if(letterDateExec) {
            //console.log(letterDateExec)
            day = parseInt(letterDateExec[3]);
            //const monthNames = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
            const monthShortNames = ['янв', 'фев', 'март', 'апр', 'май', 'июнь', 'июль', 'авг', 'сент', 'окт', 'ноя', 'дек'];
            month = monthShortNames.findIndex(x => x.toLowerCase() == letterDateExec[4].toLowerCase());
            if(isPast && (new Date(year, month, day, hours, minutes, seconds)).getTime() > Date.now()) {
                year -= 1;
            }
        }
    }
    //console.log(`year: ${year}, month: ${month}, day: ${day}, time[0]: ${time[0]}, time[1]: ${time[1]}, ${new Date(year, month, day, parseInt(time[0]), parseInt(time[1]))}`);
    return new Date(year, month, day, hours, minutes, seconds);
}
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;
            }
        }
    }
}