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.

// ==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();
    }
})();