[MWI]Guild Members Table Sorting

Add sorting functionality to the guild members table in Milky Way Idle game

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         [MWI]Guild Members Table Sorting
// @name:zh-CN   [银河奶牛]公会成员表格排序
// @namespace    https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-guid-members-table-sorting
// @version      1.0.6
// @description  Add sorting functionality to the guild members table in Milky Way Idle game
// @description:zh-CN  为银河放置游戏的公会成员表格添加排序功能
// @author       shenhuanjie
// @license      MIT
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @icon         https://www.milkywayidle.com/favicon.svg
// @grant        none
// @homepage     https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-guid-members-table-sorting
// @supportURL   https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-guid-members-table-sorting/-/issues
// ==/UserScript==

(function() {
    'use strict';

    // 判断用户语言环境
    const isChinese = navigator.language.includes('zh');

    const messages = {
        zh: {
            scriptStarted: '公会成员表格排序脚本已启动',
            tableNotFound: '未找到公会成员表格,等待用户点击重试',
            tableFound: '已找到公会成员表格,开始绑定排序功能',
            headerClicked: '第 {column} 列表头被点击,当前点击次数: {count}',
            resetSort: '第 {column} 列表头第三次被点击,重置表格排序',
            sortDirectionChanged: '第 {column} 列的排序方向切换为: {direction}',
            sortCompleted: '第 {column} 列排序完成',
            styleAdded: '已添加必要表格样式',
            hideButton: '已隐藏转让会长按钮'
        },
        en: {
            scriptStarted: 'Guild members table sorting script has been started',
            tableNotFound: 'Guild members table not found. Waiting for user click to retry',
            tableFound: 'Guild members table found. Starting to bind sorting function',
            headerClicked: 'The header of column {column} has been clicked. Current click count: {count}',
            resetSort: 'The header of column {column} has been clicked for the third time. Resetting table sorting',
            sortDirectionChanged: 'The sorting direction of column {column} has been changed to: {direction}',
            sortCompleted: 'Sorting of column {column} is completed',
            styleAdded: 'Necessary table styles have been added',
            hideButton: 'The "Transfer Guild Master" button has been hidden'
        }
    };

    function getMessage(key, replacements = {}) {
        const lang = isChinese? 'zh' : 'en';
        let message = messages[lang][key];
        for (const [placeholder, value] of Object.entries(replacements)) {
            message = message.replace(`{${placeholder}}`, value);
        }
        return message;
    }

    console.log(getMessage('scriptStarted'));

    let table = null;
    let hideTransferBtn = null;
    function initTableSort() {
        // 使用属性选择器进行模糊匹配
        table = document.querySelector('[class^="GuildPanel_membersTable__"]');
        if (!table) {
            // console.log(getMessage('tableNotFound'));
            return;
        }
        console.log(getMessage('tableFound'));

        const headers = table.querySelectorAll('th');
        let sortDirections = Array(headers.length).fill(null);
        let clickCounts = Array(headers.length).fill(0);
        const initialRows = Array.from(table.querySelectorAll('tbody tr'));

        headers.forEach((header, index) => {
            header.addEventListener('click', () => {
                clickCounts[index]++;
                // console.log(getMessage('headerClicked', { column: index + 1, count: clickCounts[index] }));

                if (clickCounts[index] === 3) {
                    // console.log(getMessage('resetSort', { column: index + 1 }));
                    const tbody = table.querySelector('tbody');
                    initialRows.forEach(row => tbody.appendChild(row));
                    headers.forEach(h => {
                        h.classList.remove('asc', 'desc');
                    });
                    sortDirections[index] = null;
                    clickCounts[index] = 0;
                    return;
                }

                const rows = Array.from(table.querySelectorAll('tbody tr'));

                if (sortDirections[index] === 'asc') {
                    sortDirections[index] = 'desc';
                } else {
                    sortDirections[index] = 'asc';
                }
                // console.log(getMessage('sortDirectionChanged', { column: index + 1, direction: sortDirections[index] }));

                headers.forEach(h => {
                    h.classList.remove('asc', 'desc');
                });

                if (sortDirections[index] === 'asc') {
                    header.classList.add('asc');
                } else {
                    header.classList.add('desc');
                }

                rows.sort((a, b) => {
                    const keyA = getCellValue(a, index);
                    const keyB = getCellValue(b, index);

                    let comparison;
                    if (typeof keyA === 'number' && typeof keyB === 'number') {
                        comparison = keyA - keyB;
                    } else if (typeof keyA ==='string' && typeof keyB ==='string') {
                        comparison = keyA.localeCompare(keyB);
                    } else if (keyA instanceof SVGElement && keyB instanceof SVGElement) {
                        const useA = keyA.querySelector('use');
                        const useB = keyB.querySelector('use');
                        const hrefA = useA? useA.getAttribute('href') : '';
                        const hrefB = useB? useB.getAttribute('href') : '';
                        comparison = hrefA.localeCompare(hrefB);
                    } else {
                        comparison = 0;
                    }

                    return sortDirections[index] === 'asc'? comparison : -comparison;
                });

                const tbody = table.querySelector('tbody');
                rows.forEach(row => tbody.appendChild(row));
                // console.log(getMessage('sortCompleted', { column: index + 1 }));
            });
        });

        const style = document.createElement('style');
        style.textContent = `
            th {
                cursor: pointer;
            }
            th.asc::after {
                content: ' ↑';
            }
            th.desc::after {
                content: ' ↓';
            }
        `;
        document.head.appendChild(style);
        console.log(getMessage('styleAdded'));
    }

    initTableSort();

    if (!table) {
        document.addEventListener('click', function clickHandler() {
            initTableSort();
            if (table) {
                document.removeEventListener('click', clickHandler);
            }
        });
    }

    if(!hideTransferBtn){
        document.addEventListener('click', function clickHandler() {
            // 隐藏转让会长按钮
            const actionButtonDivs = document.querySelectorAll('[class^="GuildPanel_actionButtons__"]');
            actionButtonDivs.forEach(div => {
                const buttons = div.querySelectorAll('button');
                buttons.forEach(button => {
                    const targetText = isChinese? '转让会长' : 'Transfer Guild Master';
                    if (button.textContent.trim() === targetText) {
                        button.style.display = 'none';
                        console.log(getMessage('hideButton'));
                        hideTransferBtn = true;
                    }
                });
            });
        });
    }

    function getCellValue(row, index) {
        const cell = row.querySelectorAll('td')[index];
        const svg = cell.querySelector('svg');
        if (svg) {
            return svg;
        }
        const text = cell.textContent.trim();
        const num = parseFloat(text);
        return isNaN(num)? text : num;
    }
})();