hwmTavernFilter

Фильтр заявок в таверне

// ==UserScript==
// @name            hwmTavernFilter
// @author          Tamozhnya1
// @namespace       Tamozhnya1
// @version         2.7
// @description     Фильтр заявок в таверне
// @include        *heroeswm.ru/tavern.php*
// @include        *lordswm.com/tavern.php*
// @grant           GM_getValue
// @grant           GM_setValue
// @grant           GM_addStyle
// @license        MIT
// ==/UserScript==

const playerIdMatch = document.cookie.match(/pl_id=(\d+)/);
if(!playerIdMatch) {
    return;
}
const PlayerId = playerIdMatch[1];
const areas = {
    "Empire Capital": { tower: 50, resources: 150 },
    "Eagle Nest": { tower: 50, resources: 150 },
    "Harbour City": { tower: 50, resources: 150 },
    "East River": { tower: 75, resources: 200 },
    "Portal Ruins": { tower: 75, resources: 200 },
    "Mithril Coast": { tower: 75, resources: 200 },
    "The Wilderness": { tower: 75, resources: 200 },
    "Tiger Lake": { tower: 150, resources: 400 },
    "Dragons' Caves": { tower: 150, resources: 400 },
    "Great Wall": { tower: 150, resources: 400 },
    "Sublime Arbor": { tower: 150, resources: 400 },
    "Rogues' Wood": { tower: 100, resources: 300 },
    "Shining Spring": { tower: 100, resources: 300 },
    "Titans' Valley": { tower: 100, resources: 300 },
    "Wolf Dale": { tower: 100, resources: 300 },
    "Sunny City": { tower: 100, resources: 300 },
    "Fishing Village": { tower: 100, resources: 300 },
    "Peaceful Camp": { tower: 125, resources: 350 },
    "Magma Mines": { tower: 125, resources: 350 },
    "Kingdom Castle": { tower: 125, resources: 350 },
    "Lizard Lowland": { tower: 200, resources: 500 },
    "Bear Mountain": { tower: 200, resources: 500 },
    "Ungovernable Steppe": { tower: 200, resources: 500 },
    "Green Wood": { tower: 100, resources: 300 },
    "Fairy Trees": { tower: 100, resources: 300 },
    "Crystal Garden": { tower: 100, resources: 300 }
};
const isEn = document.documentElement.lang == "en";
const locations = {
    "Empire Capital": "Столица Империи",
    "East River": "Восточная Река",
    "Tiger Lake": "Тигриное Озеро",
    "Rogues' Wood": "Лес Разбойников",
    "Wolf Dale": "Долина Волков",
    "Peaceful Camp": "Мирный Лагерь",
    "Lizard Lowland": "Равнина Ящеров",
    "Green Wood": "Зеленый Лес",
    "Eagle Nest": "Орлиное Гнездо",
    "Portal Ruins": "Руины Портала",
    "Dragons' Caves": "Пещеры Драконов",
    "Shining Spring": "Сияющий Родник",
    "Sunny City": "Солнечный Город",
    "Magma Mines": "Магма Шахты",
    "Bear Mountain": "Медвежья Гора",
    "Fairy Trees": "Магический Лес",
    "Harbour City": "Портовый Город",
    "Mithril Coast": "Мифриловый Берег",
    "Great Wall": "Великая Стена",
    "Titans' Valley": "Равнина Титанов",
    "Fishing Village": "Рыбачье село",
    "Kingdom Castle": "Замок Королевства",
    "Ungovernable Steppe": "Непокорная Степь",
    "Crystal Garden": "Кристальный Сад",
    "East Island": "Восточный Остров",
    "The Wilderness": "Дикие земли",
    "Sublime Arbor": "Великое Древо"
};

main();
function main() {
    const tavernRef = document.querySelector("a[href='/tavern.php']");
    if(tavernRef) {
        tavernRef.parentNode.appendChild(document.createTextNode(" / "));
        const showOptionsRef = addElement("a", { href: "javascript:void(0)", innerHTML: isEn ? "Filter settings" : "Настрока фильтра" }, tavernRef.parentNode);
        showOptionsRef.addEventListener("click", showOptions);
    }
    applyFilter();
}
function showOptions() {
    if(showPupupPanel(GM_info.script.name)) {
       return;
    }
    const fieldsMap = [];
    fieldsMap.push([addElement("b", { innerText: "Выберите районы" })]);
    let i = 0;
    let fieldsRow = [];
    for(const area in areas) {
        i++;
        const areaName = isEn ? area : locations[area];
        const areaLabel = addElement("label", { for: `areaCheckbox${i}`, innerHTML: `${areaName} (${areas[area].tower}/${areas[area].resources})` });
        const areaCheckbox = addElement("input", { id: `areaCheckbox${i}`, type: "checkbox" });
        areaCheckbox.checked = getPlayerBool(`show${area}`, true);
        areaCheckbox.addEventListener("click", function() { setPlayerValue(`show${area}`, this.checked); });

        fieldsRow.push(areaLabel);
        fieldsRow.push(areaCheckbox);
        if(i % 4 == 0) {
            fieldsMap.push(fieldsRow);
            fieldsRow = [];
        }
    }
    fieldsMap.push(fieldsRow);
    fieldsMap.push([]);

    // Уровень противника
    const levelFromCaption = addElement("b", { innerText: isEn ? "Partner level from" : "Уровень противника от" });
    const minLevelSelect = addElement("select");
    minLevelSelect.addEventListener("change", function() { setPlayerValue("minLevel", this.value); });

    const levelToCaption = addElement("b", { innerText: isEn ? "to" : "до" });
    const maxLevelSelect = addElement("select");
    maxLevelSelect.addEventListener("change", function() { setPlayerValue("maxLevel", this.value); });
    const opponentLevels = {};
    opponentLevels[-1] = isEn ? "Any" : "Любой";
    for(let i = 0; i <= 20; i++) {
        opponentLevels[i] = i.toString();
    }
    for(const opponentLevel in opponentLevels) {
        let option = addElement("option", { value: opponentLevel, innerHTML: opponentLevels[opponentLevel] }, minLevelSelect);
        if(opponentLevel == getPlayerValue("minLevel", "-1")) {
            option.setAttribute("selected", "selected");
        }
        option = addElement("option", { value: opponentLevel, innerHTML: opponentLevels[opponentLevel] }, maxLevelSelect);
        if(opponentLevel == getPlayerValue("maxLevel", "-1")) {
            option.setAttribute("selected", "selected");
        }
    }
    fieldsMap.push([levelFromCaption, minLevelSelect, levelToCaption, maxLevelSelect]);

    // Тип игры
    const gameTypeCaption = addElement("b", { innerText: isEn ? "Game type" : "Тип игры" });
    const gameTypeSelect = addElement("select");
    gameTypeSelect.addEventListener("change", function() { setPlayerValue("gameType", this.value); });
    const gameTypes = {"-1": isEn ? "Any" : "Любой", "1": isEn ? "One desk" : "Одна колода", "8": isEn ? "Infinite desk" : "Бесконечная колода"};
    for(const gameType in gameTypes) {
        const option = addElement("option", { value: gameType, innerHTML: gameTypes[gameType] }, gameTypeSelect);
        if(gameType == getPlayerValue("gameType", "-1")) {
            option.setAttribute("selected", "selected");
        }
    }
    fieldsMap.push([gameTypeCaption, gameTypeSelect]);

    // Время на ход
    const moveTimeFromCaption = addElement("b", { innerText: isEn ? "Turn time from" : "Время на ход от"});
    const minTimeSelect = addElement("select");
    minTimeSelect.addEventListener("change", function() { setPlayerValue("minTime", this.value); });

    const moveTimeToCaption = addElement("b", { innerText: isEn ? "to" : "до" });
    const maxTimeSelect = addElement("select");
    maxTimeSelect.addEventListener("change", function() { setPlayerValue("maxTime", this.value); });
    const moveTimes = {"-1": isEn ? "Any" : "Любая", "15": isEn ? "15 sec" :"15 сек.", "30": isEn ? "30 sec" : "30 сек.", "40": isEn ? "40 sec" : "40 сек."};
    for(const moveTime in moveTimes) {
        let option = addElement("option", { value: moveTime, innerHTML: moveTimes[moveTime] }, minTimeSelect);
        if(moveTime == getPlayerValue("minTime", "-1")) {
            option.setAttribute("selected", "selected");
        }
        option = addElement("option", { value: moveTime, innerHTML: moveTimes[moveTime] }, maxTimeSelect);
        if(moveTime == getPlayerValue("maxTime", "-1")) {
            option.setAttribute("selected", "selected");
        }
    }
    fieldsMap.push([moveTimeFromCaption, minTimeSelect, moveTimeToCaption, maxTimeSelect]);

    // Величина ставки
    const betValueFromCaption = addElement("b", { innerText: isEn ? "Bet value from" : "Величина ставки от" });
    const minBetSelect = addElement("select");
    minBetSelect.addEventListener("change", function() { setPlayerValue("minBet", this.value); });

    const betValueToCaption = addElement("b", { innerText: isEn ? "to" : "до" });
    const maxBetSelect = addElement("select");
    maxBetSelect.addEventListener("change", function() { setPlayerValue("maxBet", this.value); });
    const betValues = { "-1": isEn ? "Any" : "Любая", "0": "0", "40": "40", "100": "100", "300": "300", "600": "600", "1000": "1000",
        "2000": "2000", "3000": "3000", "4000": "4000", "5000": "5000", "6000": "6000", "7000": "7000",
        "10000": "10000", "11000": "11000", "12000": "12000", "20000": "20000", "25000": "25000", "30000": "30000",
        "35000": "35000", "40000": "40000", "50000": "50000" };
    for(const betValue in betValues) {
        let option = addElement("option", { value: betValue, innerHTML: betValues[betValue] }, minBetSelect);
        if(betValue == getPlayerValue("minBet", "-1")) {
            option.setAttribute("selected", "selected");
        }
        option = addElement("option", { value: betValue, innerHTML: betValues[betValue] }, maxBetSelect);
        if(betValue == getPlayerValue("maxBet", "-1")) {
            option.setAttribute("selected", "selected");
        }
    }
    fieldsMap.push([betValueFromCaption, minBetSelect, betValueToCaption, maxBetSelect]);

    createPupupPanel(GM_info.script.name, getScriptReferenceHtml() + " " + getSendErrorMailReferenceHtml(), fieldsMap, onScriptOptionToggle);
}
function onScriptOptionToggle(isShown) {
    if(isShown) {
        setTimeout("clearTimeout(Timer)", 0); // Вкл/выкл таймер обновления страницы (взаимодействие со скриптом на странице)
    } else {
        setTimeout("Refresh()", 0);
        applyFilter();
    }
}
function applyFilter() {
    const options = { minBet: parseInt(getPlayerValue("minBet", "-1")), maxBet: parseInt(getPlayerValue("maxBet", "-1")), gameType: parseInt(getPlayerValue("gameType", -1)), minLevel: parseInt(getPlayerValue("minLevel", -1)), maxLevel: parseInt(getPlayerValue("maxLevel", -1)), minTime: parseInt(getPlayerValue("minTime", -1)), maxTime: parseInt(getPlayerValue("maxTime", -1)) };
    //console.log(options);
    let currentTavern;
    const bets = Array.from(document.querySelectorAll("table[class='wb'] > tbody > tr")).filter(x => x.querySelector("img[src*='gold.png']")).map(x => {
        if(x.cells.length == 6) {
            if(!x.cells[0].hasAttribute("location")) {
                x.cells[0].setAttribute("location", x.cells[0].innerHTML);
            }
            currentTavern = x.cells[0].getAttribute("location");
            const locationName = isEn ? currentTavern : locations[currentTavern];
            x.cells[0].innerHTML = `${locationName}<br><span title="${isEn ? "Tower height" : "Высота башни"}">${areas[currentTavern].tower}</span> / <span title="${isEn ? "Resources" : "Ресурсов"}">${areas[currentTavern].resources}</span>`;
        }
        const bet = {
            tavern: currentTavern,
            level: parseInt(x.querySelector("a[href^='pl_info.php']").parentNode.querySelector("i").innerText.replace("(", "").replace(")", "")),
            gameType: x.querySelector("img[src*='1koloda']") ? 1 : 8,
            time: parseInt((new RegExp(`(\\d+) ${isEn ? "sec" : "сек"}.`)).exec(x.innerHTML)[1]),
            betValue: parseInt(x.querySelector("img[src*='gold.png']").parentNode.nextElementSibling.innerText.replace(",", "")),
            mayEnter: x.querySelector("a[href^='join_to_card_game.php']") ? true : false
        };
        bet.enabled = bet.mayEnter
            && (options.gameType == -1 || options.gameType == bet.gameType)
            && (options.minLevel == -1 || options.minLevel <= bet.level)
            && (options.maxLevel == -1 || options.maxLevel >= bet.level)
            && (options.minTime == -1 || options.minTime <= bet.time)
            && (options.maxTime == -1 || options.maxTime >= bet.time)
            && (options.minBet == -1 || options.minBet <= bet.betValue)
            && (options.maxBet == -1 || options.maxBet >= bet.betValue);
        x.style.display = getPlayerBool(`show${bet.tavern}`, true) ? "" : "none";
        x.style.backgroundColor = bet.enabled ? "green" : "gray";
        if(bet.mayEnter) {
            x.querySelector("a[href^='join_to_card_game.php']").style.pointerEvents = bet.enabled ? "" : "none";
        }
        //console.log(bet);
        return bet;
    });
}
// API
function createPupupPanel(panelName, panelTitle, fieldsMap, panelToggleHandler) {
    const backgroundPopupPanel = addElement("div", { id: panelName, style: "position: fixed; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4); z-index: 200;" }, document.body);
    backgroundPopupPanel.addEventListener("click", function(e) { if(e.target == this) { hidePupupPanel(panelName, panelToggleHandler); }});
    const contentDiv = addElement("div", { style: `top: 50%; transform: translateY(-50%); padding: 5px; display: flex; flex-wrap: wrap; position: relative; margin: auto; padding: 0; width: fit-content; background-image: linear-gradient(to right, #eea2a2 0%, #bbc1bf 19%, #57c6e1 42%, #b49fda 79%, #7ac5d8 100%); border: 1mm ridge rgb(211, 220, 50);` }, backgroundPopupPanel);
    if(panelTitle) {
        addElement("b", { innerHTML: panelTitle, style: "text-align: center; margin: auto; width: 90%; display: block;" }, contentDiv);
    }
    const divClose = addElement("span", { id: panelName + "close", title: isEn ? "Close" : "Закрыть", innerHTML: "&times;", style: "cursor: pointer; font-size: 20px; font-weight: bold;" }, contentDiv);
    divClose.addEventListener("click", function() { hidePupupPanel(panelName, panelToggleHandler); });

    addElement("div", { style: "flex-basis: 100%; height: 0;"}, contentDiv);

    if(fieldsMap) {
        let contentTable = addElement("table", { style: "flex-basis: 100%; width: min-content;"}, contentDiv);
        for(const rowData of fieldsMap) {
            if(rowData.length == 0) { // Спомощью передачи пустой стороки-массива, указываем, что надо начать новую таблицу после брейка
                addElement("div", { style: "flex-basis: 100%; height: 0;"}, contentDiv);
                contentTable = addElement("table", undefined, contentDiv);
                continue;
            }
            const row = addElement("tr", undefined, contentTable);
            for(const cellData of rowData) {
                const cell = addElement("td", undefined, row);
                if(cellData) {
                    if(typeof(cellData) == "string") {
                        cell.innerText = cellData;
                    } else {
                        cell.appendChild(cellData);
                    }
                }
            }
        }
    }
    if(panelToggleHandler) {
        panelToggleHandler(true);
    }
    return contentDiv;
}
function showPupupPanel(panelName, panelToggleHandler) {
    const backgroundPopupPanel = document.getElementById(panelName);
    if(backgroundPopupPanel) {
        backgroundPopupPanel.style.display = '';
        if(panelToggleHandler) {
            panelToggleHandler(true);
        }
        return true;
    }
    return false;
}
function hidePupupPanel(panelName, panelToggleHandler) {
    const backgroundPopupPanel = document.getElementById(panelName);
    backgroundPopupPanel.style.display = 'none';
    if(panelToggleHandler) {
        panelToggleHandler(false);
    }
}
function getValue(key, defaultValue) { return GM_getValue(key, defaultValue); };
function setValue(key, value) { GM_setValue(key, value); };
function deleteValue(key) { return GM_deleteValue(key); };
function getPlayerValue(key, defaultValue) { return GM_getValue(`${key}${PlayerId}`, defaultValue); };
function setPlayerValue(key, value) { GM_setValue(`${key}${PlayerId}`, value); };
function deletePlayerValue(key) { return GM_deleteValue(`${key}${PlayerId}`); };
function listValues() { return GM_listValues(); }
function getPlayerBool(valueName, defaultValue = false) { return getBool(valueName + PlayerId, defaultValue); }
function getBool(valueName, defaultValue = false) {
    const value = getValue(valueName);
    //console.log(`valueName: ${valueName}, value: ${value}, ${typeof(value)}`)
    if(value != undefined) {
        if(typeof(value) == "string") {
            return value == "true";
        }
        if(typeof(value) == "boolean") {
            return value;
        }
    }
    return defaultValue;
}
function GM_addStyle(css) { addElement("style", { type: "text/css", innerHTML: css }, document.head); }
function addElement(type, data = {}, parent = undefined, insertPosition = "beforeend") {
    const el = document.createElement(type);
    for(const key in data) {
        if(key == "innerText" || key == "innerHTML") {
            el[key] = data[key];
        } else {
            el.setAttribute(key, data[key]);
        }
    }
    if(parent) {
        parent.insertAdjacentElement(insertPosition, el);
    }
    return el;
}
function getScriptLastAuthor() {
    let authors = GM_info.script.author;
    if(!authors) {
        const authorsMatch = GM_info.scriptMetaStr.match(/@author(.+)\n/);
        authors = authorsMatch ? authorsMatch[1] : "";
    }
    const authorsArr = authors.split(",").map(x => x.trim()).filter(x => x);
    return authorsArr[authorsArr.length - 1];
}
function getDownloadUrl() {
    let result = GM_info.script.downloadURL;
    if(!result) {
        const downloadURLMatch = GM_info.scriptMetaStr.match(/@downloadURL(.+)\n/);
        result = downloadURLMatch ? downloadURLMatch[1] : "";
        result = result.trim();
    }
    return result;
}
function getScriptReferenceHtml() { return `<a href="${getDownloadUrl()}" title="${isEn ? "Check for update" : "Проверить обновление скрипта"}" target=_blanc>${GM_info.script.name} ${GM_info.script.version}</a>`; }
function getSendErrorMailReferenceHtml() { return `<a href="sms-create.php?mailto=${getScriptLastAuthor()}&subject=${isEn ? "Error in" : "Ошибка в"} ${GM_info.script.name} ${GM_info.script.version} (${GM_info.scriptHandler} ${GM_info.version})" target=_blanc>${isEn ? "Bug report" : "Сообщить об ошибке"}</a>`; }