hwmAdvancedMenu

Расширенное меню

目前為 2023-12-26 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name           hwmAdvancedMenu
// @namespace      Tamozhnya1
// @author         Tamozhnya1
// @description    Расширенное меню
// @version        1.8
// @include        *heroeswm.ru/*
// @include        *lordswm.com/*
// @exclude        */rightcol.php*
// @exclude        */ch_box.php*
// @exclude        */chat*
// @exclude        */ticker.html*
// @exclude        */frames*
// @exclude        */brd.php*
// @license        MIT
// ==/UserScript==

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]; };
}
const StoredForumTreadsAmount = 10;
const playerIdMatch = document.cookie.match(/pl_id=(\d+)/);
if(!playerIdMatch) {
    return;
}
const PlayerId = playerIdMatch[1];
const isEn = document.documentElement.lang == "en";
const isNewInterface = document.querySelector("div#hwm_header") ? true : false;

main();
function main() {
    processHouses();
    processQuickLinks();
    processForum();
    checkMilitaryClan();
    // После рынка вставим ссылки на ресурсы, с задержкой в пол секунды
    const auctionRef = isNewInterface ? document.querySelector("div.sh_dd_container a[href='auction.php']") : getParent(document.querySelector("li > a[href='auction.php']"), "li");
    if(auctionRef) {
        let resourcesShowTimer;
        let resourcesShown = false;
        const resources = [ { type: "1", name: isEn ? 'Wood' : 'Древесина' }, { type: "2", name: isEn ? 'Ore' : 'Руда' }, { type: "3", name: isEn ? 'Mercury' : 'Ртуть' }, { type: "4", name: isEn ? 'Sulfur' : 'Сера' }, { type: "5", name: isEn ? 'Crystals' : 'Кристаллы' }, { type: "6", name: isEn ? 'Gems' : 'Самоцветы' } ];
        auctionRef.addEventListener("mouseover", function() { if(!resourcesShown) resourcesShowTimer = setTimeout(function() {
            const html = resources.reduce((t, x) => t + getMenuItemTemplate(`auction.php?cat=res&sort=0&type=${x.type}`, `  ${x.name}`), "");
            auctionRef.insertAdjacentHTML('afterend', html);
            resourcesShown = true;
        }, 500) } );
        auctionRef.addEventListener("mouseout", function() { clearTimeout(resourcesShowTimer); });
    }
    // После передачи ресурсов вставим основные ссылки персонажа
    const transferRef = isNewInterface ? document.querySelector("div.sh_dd_container a[href='transfer.php']") : getParent(document.querySelector("li > a[href='transfer.php']"), "li");
    if(transferRef) {
        const personalReferences = [
            { href: `el_transfer.php`, text: isEn ? 'Transfer elements' : 'Передача элементов' },
            { href: 'javascript:void(0);', text: "" },
            { href: `pl_info.php?id=${PlayerId}`, text: isEn ? 'Character' : 'Персонаж' },
            { href: `pl_transfers.php?id=${PlayerId}`, text: isEn ? 'Transfer log' : 'Протокол передач' },
            { href: `pl_warlog.php?id=${PlayerId}`, text: isEn ? 'Combat log' : 'Протокол боев' },
            { href: `pl_cardlog.php?id=${PlayerId}`, text: isEn ? 'Game log' : 'Протокол игр' },
            { href: `friends.php`, text: isEn ? 'Your friends' : 'Ваши друзья' },
            { href: `/pl_clans.php`, text: isEn ? 'Your clans' : 'Ваши кланы' },
            { href: `ephoto_albums.php`, text: isEn ? 'Your photos' : 'Ваш фотоальбом' },
            { href: 'javascript:void(0);', text: "" },
            { href: `logout.php?${Math.round( Math.random()* 100000 )}`, text: isEn ? 'Logout' : 'Выход' }
        ];
        const html = personalReferences.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text), "");
        transferRef.insertAdjacentHTML('afterend', html);
    }
    // Расширение карты
    const mapMenuContainer = isNewInterface ? document.querySelector("div.sh_dd_container a[href='map.php?st=hs']") : getParent(document.querySelector("li > a[href='map.php?st=hs']"), "li");
    if(mapMenuContainer) {
        const housesInfo = JSON.parse(GM_getValue(`PlayerHouses${PlayerId}`, "{}"));
        let mapExtenders = Object.keys(housesInfo).map(x => ({ href: `house_info.php?id=${x}`, text: housesInfo[x].replace(" ", " ") }));
        // Арендованные дома. Берутся из скрипта Transporter
        for(let locationNumber = 1; locationNumber <= 27; locationNumber++) {
            const guestInfo = JSON.parse(GM_getValue(`GuestInfo${locationNumber}`, "{}"));
            for(const key in guestInfo) {
                mapExtenders = [...mapExtenders, { href: `house_info.php?id=${key}`, text: guestInfo[key].HostInfo, title: `до ${(new Date(guestInfo[key].ExpireDate)).toLocaleString()}` }];
            }
        }
        //
        mapExtenders = [...mapExtenders, { href: 'javascript:void(0);', text: "" }, { href: 'ecostat.php', text: isEn ? 'Economic statistics' : 'Эконом. статистика' }];
        // Ссылки по боевому клану. Появляются после захода на страницу кланов
        if(GM_getValue(`MilitaryClanId${PlayerId}`)) {
            mapExtenders = [...mapExtenders, { href: 'javascript:void(0);', text: "" },
                { href: `/clan_info.php?id=${GM_getValue(`MilitaryClanId${PlayerId}`)}`, text: isEn ? 'Military clan' : 'Боевой клан' },
                { href: `/sklad_info.php?clan_id=${GM_getValue(`MilitaryClanId${PlayerId}`)}`, text: isEn ? 'Clan depository' : 'Клан-склад' },
                { href: `/sms_clans.php?clan_id=${GM_getValue(`MilitaryClanId${PlayerId}`)}`, text: isEn ? 'Clan post' : 'Клановая рассылка' }
            ];
        }
        const html = mapExtenders.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text, x.title), "");
        //console.log(mapExtenders)
        mapMenuContainer.insertAdjacentHTML('afterend', html);
    }
    // Добавим форумов и дейли
    const forumsContainer = isNewInterface ? document.querySelector("div.sh_dd_container a[href='forum.php#t1']") : getParent(document.querySelector("li > a[href='forum.php#t1']"), "li");
    if(forumsContainer) {
        const forumExtenders = [
            { href: `forum_thread.php?id=${isEn ? '103' : '3'}`, text: isEn ? 'Ideas and suggestions' : 'Идеи и предложения' },
            { href: `forum_thread.php?id=${isEn ? '121' : '22'}`, text: isEn ? 'Smiths and Ench. services' : 'Услуги кузнецов и оруж.' },
            { href: 'javascript:void(0);', text: "" },
            { href: `${isEn ? 'http://daily.heroeswm.ru/newscom.php' : 'http://daily.heroeswm.ru/'}`, text: isEn ? 'HWM Daily ENG' : 'Геройская лента' }
        ];
        let html = forumExtenders.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text), "");
        const lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]"));
        if(lastForumTreads.length > 0) {
            html += getMenuItemTemplate('javascript:void(0);', "");
        }
        html += lastForumTreads.reduce((t, x) => t + getMenuItemTemplate(`/forum_messages.php?tid=${x.threadId}${x.pageIndex ? `&page=${x.pageIndex}` : ""}`, x.threadName, "", `forumReference${x.threadId}`), "");
        forumsContainer.insertAdjacentHTML('afterend', html);
        Array.from(document.querySelectorAll(`span[id^='forumReference']`)).forEach(x => x.addEventListener("click", deleteForumReferenceMenuItem));
    }
    // После чатов добавим быстрые ссылки
    const framesContainer = isNewInterface ? document.querySelector("div.sh_dd_container a[href='frames.php?room=4']") : getParent(document.querySelector("li > a[href='frames.php?room=4']"), "li");
    if(framesContainer) {
        //GM_deleteValue(`QuickLinks${PlayerId}`);
        let quickLinks = JSON.parse(GM_getValue(`QuickLinks${PlayerId}`, "[]")).filter(x => x.Name != "" && x.Refernce != "");
        if(quickLinks.length > 0) {
            quickLinks = [{ Refernce: "javascript:void(0);", Name: "" }, ...quickLinks];
            const html = quickLinks.reduce((t, x) => t + getMenuItemTemplate(x.Refernce, x.Name), "");
            framesContainer.insertAdjacentHTML('afterend', html);
        }
    }
}
function deleteForumReferenceMenuItem(e) {
    //e.stopPropagation();
    e.preventDefault();
    const threadId = e.target.id.replace("forumReference", "");
    //console.log(`threadId: ${threadId}`)
    const lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]")).filter(x => x.threadId != threadId);
    GM_setValue("LastForumTreads", JSON.stringify(lastForumTreads));
    
    const menuItem = isNewInterface ? getParent(e.target, "a") : getParent(e.target, "li");
    //console.log(menuItem)
    menuItem.remove();
}
function processForum() {
    if(location.pathname == '/forum_messages.php') {
        if(GM_getValue("LastForumTreads")) {
            const saved = JSON.parse(GM_getValue("LastForumTreads"));
            if(!Array.isArray(saved)) {
                GM_deleteValue("LastForumTreads");
            }
        }
        //https://www.heroeswm.ru/forum_messages.php?tid=2964583&page=5
        const threadId = getUrlParamValue(location.href, "tid");
        const pageIndex = getUrlParamValue(location.href, "page");
        const threadName = document.querySelector(`a[href='forum_messages.php?tid=${threadId}'`).innerText;
        const newThread = { threadId: threadId, pageIndex: pageIndex || 0, threadName: threadName, viewTime: Date.now() };
        let lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]"));
        const thisThread = lastForumTreads.find(x => x.threadId == newThread.threadId && x.pageIndex == newThread.pageIndex);
        if(thisThread) {
            window.scrollTo(0, thisThread.scrollPosition); 
        }
        newThread.scrollPosition = window.scrollY;
        lastForumTreads = lastForumTreads.filter(x => x.threadId != newThread.threadId);
        lastForumTreads.unshift(newThread);
        lastForumTreads = lastForumTreads.slice(0, StoredForumTreadsAmount);
        GM_setValue("LastForumTreads", JSON.stringify(lastForumTreads));
        
        document.addEventListener("scroll", (event) => {
            const lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]"))
            const scrolledTread = lastForumTreads.find(x => x.threadId == newThread.threadId);
            if(scrolledTread) {
                scrolledTread.scrollPosition = window.scrollY;
                GM_setValue("LastForumTreads", JSON.stringify(lastForumTreads));
            }
        });
    }
}
function getMenuItemTemplate(href, text, title, deleteId) {
    if(text.length > 30) {
        if(!title) {
            title = text;
        }
        text = text.substring(0, 30) + "...";
    }
    if(!isNewInterface) {
        if(text == "") {
            return "<hr>";
        }
        let deleteElementText = "";
        if(deleteId) {
            deleteElementText = `<span id="${deleteId}" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; color: yellow;">[x]</span>`;
        }
        return `<li><a href='${href}' title='${title || ""}' style="">${text}${deleteElementText}</a></li>`;
    } else {
        let deleteElementText = "";
        if(deleteId) {
            deleteElementText = `<span id="${deleteId}" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; float: right;">[x]</span>`;
        }
        return `<a href='${href}' title='${title || ""}' style='text-decoration: none;'><div style='${text == "" ? "padding: 0; height: 2px;" : ""}'>${text}${deleteElementText}</div></a>`;
    }
}
function processHouses() {
    if(location.pathname == "/pl_info_realty.php" && getUrlParamValue(location.href, "id") == PlayerId) {
        const housesInfo = Array.from(document.querySelectorAll("a[href^='house_info.php']")).reduce((t, x) => ({...t, [getUrlParamValue(x.href, "id")]: getParent(x, "tr").cells[4].innerText }), {});
        //console.log(housesInfo);
        GM_setValue(`PlayerHouses${PlayerId}`, JSON.stringify(housesInfo));
    }
}
function processQuickLinks() {
    if(location.pathname == "/pers_navlinks.php") {
        const tbody = getParent(document.querySelector("form[action='pers_navlinks.php']"), "tbody");
        const tr = addElement("tr", tbody);
        const td = addElement("td", tr);
        const table = addElement("table", td);
        
        const quickLinkAmount = 10;
        let quickLinks = JSON.parse(GM_getValue(`QuickLinks${PlayerId}`, `[${Array(quickLinkAmount).fill('{"Name":"","Refernce":""}').join()}]`));
        if(quickLinks.length != quickLinkAmount) {
            GM_deleteValue(`QuickLinks${PlayerId}`);
            quickLinks = JSON.parse(`[${Array(quickLinkAmount).fill('{"Name":"","Refernce":""}').join()}]`);
        }
        //console.log(quickLinks)
        let i = 0;
        for(const quickLink of quickLinks) {
            const html = `
<tr>
    <td>
        <input id="linkName${i}" name="linkName" type="text" style="width: 200px;" />
        <input id="linkValue${i}" name="linkValue" type="text" style="width: 300px;" />
    </td>
</tr>`;
            table.insertAdjacentHTML('beforeend', html);
            table.querySelector(`#linkName${i}`).value = quickLink.Name;
            table.querySelector(`#linkValue${i}`).value = quickLink.Refernce;
            table.querySelector(`#linkName${i}`).addEventListener("change", saveQuickLinks);
            table.querySelector(`#linkValue${i}`).addEventListener("change", saveQuickLinks);
            i++;
        }
    }
}
function saveQuickLinks() {
    const quickLinks = Array.from(document.querySelectorAll("input[name=linkName]")).map(x => ({ Name: x.value, Refernce: document.getElementById(x.id.replace("linkName", "linkValue")).value }));
    //console.log(quickLinks)
    GM_setValue(`QuickLinks${PlayerId}`, JSON.stringify(quickLinks));
}
async function checkMilitaryClan() {
    if(location.pathname == '/pl_clans.php') {
        const doc = location.pathname == '/pl_clans.php' ? document : await getRequest(`/pl_clans.php`);
        if(location.pathname == '/pl_clans.php') {
            GM_deleteValue(`MilitaryClanId${PlayerId}`);
        }
        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, Ref: x.href }; });
        for(const clanInfo of clanInfos) {
            const clanInfoDoc = await getRequest(clanInfo.Ref);
            if(clanInfoDoc.body.innerHTML.includes(isEn ? "[Military clan]" : "[боевой клан]")) {
                GM_setValue(`MilitaryClanId${PlayerId}`, clanInfo.Id);
                break;
            }
        }
    }
    if(!GM_getValue(`MilitaryClanId${PlayerId}`)) {
        console.log("Вы не состоите в боевом клане");
        return false;
    }
    return true;
}
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 getParent(element, parentType, number = 1) {
    if(!element) {
        return;
    }
    let result = element;
    let foundNumber = 0;
    while(result = result.parentNode) {
        if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
            foundNumber++;
            if(foundNumber == number) {
                return result;
            }
        }
    }
}
function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }