WOW Cross Link : Raider.IO, WoWProgress, Warcraft Logs (Characters & Guilds)

Ajoute liens croisés entre Raider.IO, WoWProgress, Warcraft Logs pour personnages et guildes, avec support SPA Raider.IO et bouton stylé Warcraft Logs guild.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         WOW Cross Link : Raider.IO, WoWProgress, Warcraft Logs (Characters & Guilds)
// @namespace    https://tampermonkey.net/
// @version      3.1
// @description  Ajoute liens croisés entre Raider.IO, WoWProgress, Warcraft Logs pour personnages et guildes, avec support SPA Raider.IO et bouton stylé Warcraft Logs guild.
// @match        https://raider.io/characters/*
// @match        https://raider.io/guilds/*
// @match        https://www.wowprogress.com/character/*
// @match        https://www.wowprogress.com/guild/*
// @match        https://www.warcraftlogs.com/character/*
// @match        https://www.warcraftlogs.com/guild/*
// @grant        none
// @license      MIT
// ==/UserScript==

// Script 1: Raider.IO → WoWProgress
(function () {
    'use strict';

    function formatRealm(realm) {
        return realm
            .replace(/([a-z])([A-Z])/g, '$1-$2')
            .replace(/([a-zA-Z])(\d)/g, '$1-$2')
            .toLowerCase();
    }

    function createIconLink(url, title) {
        const link = document.createElement('a');
        link.href = url;
        link.title = title;
        link.target = '_blank';
        link.rel = 'noopener';
        link.className = 'slds-avatar slds-avatar--small slds-m-left--large rio-profile-links wowprogress';

        const icon = document.createElement('img');
        icon.src = 'https://www.wowprogress.com/favicon.ico';
        icon.alt = 'WoWProgress';
        icon.style.width = '100%';
        icon.style.height = '100%';
        icon.style.borderRadius = '50%';

        link.appendChild(icon);
        return link;
    }

    const interval = setInterval(() => {
        const path = window.location.pathname;
        const parts = path.split('/');
        const type = parts[1];
        const region = parts[2];
        const rawRealm = parts[3];
        const name = parts[4];
        const realm = formatRealm(rawRealm);

        let container = null;
        let targetSelector = '';

        if (type === 'characters') {
            targetSelector = 'header.slds-clearfix .slds-float--right';
        } else if (type === 'guilds') {
            targetSelector = 'th.slds-clearfix .slds-float--right';
        } else {
            return;
        }

        container = document.querySelector(targetSelector);
        if (!container) return;

        clearInterval(interval);

        const wpURL =
            type === 'characters'
                ? `https://www.wowprogress.com/character/${region}/${realm}/${name}`
                : `https://www.wowprogress.com/guild/${region}/${realm}/${name}`;

        const wpIcon = createIconLink(wpURL, 'Voir sur WoWProgress');
        container.appendChild(wpIcon);
    }, 300);
})();

// Script 2: WarcraftLogs → WoWProgress
(function () {
    'use strict';

    const WOWPROGRESS_ICON = 'https://www.wowprogress.com/favicon.ico';

    const capitalizeFirst = str => str ? str[0].toUpperCase() + str.slice(1) : '';

    const createLink = (url, imgClass, textClass, text) => {
        const a = document.createElement('a');
        a.href = url;
        a.target = '_blank';
        a.rel = 'noopener noreferrer';

        const img = document.createElement('img');
        img.src = WOWPROGRESS_ICON;
        img.alt = 'WoWProgress';
        img.className = imgClass;

        a.appendChild(img);
        if (text) {
            const span = document.createElement('span');
            span.textContent = text;
            span.className = textClass;
            a.appendChild(span);
        }

        return a;
    };

    const addCharLink = () => {
        const charLink = document.querySelector('#character-name a.character-name-link');
        const serverLink = document.querySelector('#guild-and-server a#server-link');
        const container = document.querySelector('#gear-box-external-links');
        if (!charLink || !serverLink || !container) return;

        const charName = encodeURIComponent(capitalizeFirst(charLink.textContent.trim()));
        const match = serverLink.textContent.trim().match(/^(.+)\s+\((.+)\)$/);
        if (!match) return;

        const server = match[1].toLowerCase().replace(/\s+/g, '-');
        const region = match[2].toLowerCase();
        const url = `https://www.wowprogress.com/character/${region}/${server}/${charName}`;

        if (!container.querySelector(`a[href="${url}"]`)) {
            const a = createLink(url, 'external-icon');
            a.style.marginLeft = '6px';
            a.style.width = '20px';
            a.style.height = '20px';
            container.appendChild(a);
        }
    };

    const addGuildLink = () => {
        const waitFor = (selectors, callback, timeout = 10000) => {
            const start = Date.now();
            const interval = setInterval(() => {
                const els = selectors.map(s => document.querySelector(s));
                if (els.every(Boolean)) {
                    clearInterval(interval);
                    callback(...els);
                } else if (Date.now() - start > timeout) {
                    clearInterval(interval);
                }
            }, 300);
        };

        const getNavMenu = () =>
            document.querySelector('.navigation ul.filter-bar-menu') ||
            document.querySelector('.guild-header__child-guilds .navigation ul.filter-bar-menu');

        waitFor(['.guild-header__name', '.guild-header__region-and-server a'], (nameEl, regionEl) => {
            const nav = getNavMenu();
            if (!nav || [...nav.querySelectorAll('a')].some(a => a.href.includes('wowprogress.com'))) return;

            const guild = nameEl.textContent.trim().replace(/\s+/g, ' ');
            const match = regionEl.textContent.trim().match(/^([A-Z]{2})\s*-\s*(.+)$/i);
            if (!match) return;

            const region = match[1].toLowerCase();
            const server = match[2].trim().toLowerCase().replace(/[\s']/g, '-');
            const guildUrl = guild.replace(/ /g, '+');
            const url = `https://www.wowprogress.com/guild/${region}/${server}/${guildUrl}`;

            const armoryLi = [...nav.querySelectorAll('li')].find(li =>
                li.textContent.includes('Armory') &&
                li.querySelector('a')?.href.includes('worldofwarcraft.com')
            );
            if (!armoryLi) return;

            const li = document.createElement('li');
            li.className = 'navigation__end-link';
            const link = createLink(url, 'guild-navigation__related-link-icon', 'guild-navigation__related-link-text', 'WowProgress');
            link.className = 'guild-navigation__related-link';
            li.appendChild(link);
            armoryLi.after(li);
        });
    };

    if (location.pathname.startsWith('/character/')) addCharLink();
    if (location.pathname.startsWith('/guild/')) addGuildLink();
})();

// Script 3: WoWProgress → Raider.IO & WarcraftLogs
(function() {
    'use strict';

    const raiderIOFavicon = 'https://raider.io/favicon.ico';
    const wclFavicon = 'https://blzgdapipro-a.akamaihd.net/favicon/wow.ico';

    function createLink(href, text, favicon, id) {
        const a = document.createElement('a');
        a.href = href;
        a.target = '_blank';
        a.rel = 'noopener noreferrer';
        a.style.display = 'inline-flex';
        a.style.alignItems = 'center';
        a.style.marginLeft = '6px';
        a.style.color = 'inherit';
        a.style.textDecoration = 'none';
        if (id) a.id = id;

        const img = document.createElement('img');
        img.src = favicon;
        img.width = 16;
        img.height = 16;
        img.style.marginRight = '4px';
        img.style.verticalAlign = 'middle';

        a.appendChild(img);
        a.appendChild(document.createTextNode(text));
        return a;
    }

    function formatServer(s) {
        return s.toLowerCase();
    }

    function formatGuildForUrl(guildRaw) {
        let decoded = decodeURIComponent(guildRaw.replace(/\+/g, ' '));
        return encodeURIComponent(decoded);
    }

    function formatGuildForWcl(guildRaw) {
        let decoded = decodeURIComponent(guildRaw.replace(/\+/g, ' '));
        return encodeURIComponent(decoded.toLowerCase());
    }

    function findArmoryLink() {
        const links = Array.from(document.querySelectorAll('a'));
        return links.find(a => a.textContent.trim().toLowerCase() === '(armory)');
    }

    function injectLinksCharacter() {
        const path = location.pathname.split('/');
        if(path.length < 5) return;

        const region = path[2];
        const rawServer = path[3];
        const character = path[4];

        const server = formatServer(rawServer);

        if(document.getElementById('extra-links-character')) return;

        const armoryLink = findArmoryLink();
        if(!armoryLink) return;

        const parent = armoryLink.parentNode;

        const raiderLink = createLink(`https://raider.io/characters/${region}/${server}/${character}`, 'Raider.IO', raiderIOFavicon);
        const wclLink = createLink(`https://www.warcraftlogs.com/character/${region}/${server}/${character.toLowerCase()}`, 'WarcraftLogs', wclFavicon);

        const container = document.createElement('span');
        container.id = 'extra-links-character';
        container.style.whiteSpace = 'nowrap';
        container.style.fontSize = '0.9em';
        container.style.marginLeft = '8px';

        container.appendChild(document.createTextNode(' | '));
        container.appendChild(raiderLink);
        container.appendChild(document.createTextNode(' | '));
        container.appendChild(wclLink);

        parent.insertBefore(container, armoryLink.nextSibling);
    }

    function injectLinksGuild() {
        const path = location.pathname.split('/');
        if(path.length < 5) return;

        const region = path[2];
        const rawServer = path[3];
        const rawGuild = decodeURIComponent(location.pathname.substring(location.pathname.indexOf(rawServer) + rawServer.length + 1)).replace(/^\/+/, '');

        const server = formatServer(rawServer);
        const guildForRaid = formatGuildForUrl(rawGuild);
        const guildForWcl = formatGuildForWcl(rawGuild);

        if(document.getElementById('extra-links-guild')) return;

        const armoryLink = findArmoryLink();
        if(!armoryLink) return;

        const parent = armoryLink.parentNode;

        const raiderLink = createLink(`https://raider.io/guilds/${region}/${server}/${guildForRaid}`, 'Raider.IO', raiderIOFavicon);
        const wclLink = createLink(`https://www.warcraftlogs.com/guild/${region}/${server}/${guildForWcl}`, 'WarcraftLogs', wclFavicon);

        const container = document.createElement('span');
        container.id = 'extra-links-guild';
        container.style.whiteSpace = 'nowrap';
        container.style.fontSize = '0.9em';
        container.style.marginLeft = '8px';

        container.appendChild(document.createTextNode(' | '));
        container.appendChild(raiderLink);
        container.appendChild(document.createTextNode(' | '));
        container.appendChild(wclLink);

        parent.insertBefore(container, armoryLink.nextSibling);
    }

    if(location.pathname.startsWith('/character/')) {
        injectLinksCharacter();
    } else if(location.pathname.startsWith('/guild/')) {
        injectLinksGuild();
    }
})();