RisiOnche

Risibank sur onche.org (Fix)

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         RisiOnche
// @namespace    RisiOnche
// @version      0.94.0
// @description  Risibank sur onche.org (Fix)
// @author       RisiOnche, Catalyst
// @match        https://onche.org/topic/*
// @match        https://onche.org/forum/*
// @match        https://onche.org/chat/*
// @match        https://onche.org/profil*
// @exclude      https://onche.org/forum/**/search
// @exclude      https://onche.org/forum/**/search?*
// @icon         https://www.risishack.com/lfoubw.png
// @grant        none
// ==/UserScript==


(async () => {

    //CSS
    const CSS = document.createElement('style');
    CSS.id = "risi-onche-css";
    CSS.textContent = `
    #risi-onche iframe {
        height: 10rem !important
    }

    /*unset force width on Shackimg */
    .preview ._solo .smiley img[src^="https://image.noelshack.com/"] {
       width: unset !important;
    }

    /* hide class risi onche close*/
    #risi-onche:not(.risi-onche-open) {
        display : none;
    }

    /* Replace stickers when risibank is open */
    #post .composer__stickers-host:has(+ #risi-onche.risi-replace-stickers.risi-onche-open) {
        display: none;
    }

    /*BTN RISI CSS PANEL*/
    #risi-onche-panel .button {
        font-size: .9rem;
    }

    /*BTN RISI CSS MP*/
    .chat .risibank-toggle {
        cursor: pointer;
        padding: 10px 20px 7px;
    }

    /* RisiMobile Options */
    @media (max-width: 880px) {
        #right .bloc.border.red {
            order: -1;
        }
    }

    /*MOBILE BTN ENVOYER CSS*/
    @media (max-width: 415px) {
        .bloc #post {
            padding-left: 5px; /* Padding for post */
            padding-right: 5px; /* reduce on mobile */
        }
        .forum .composer__send {
            padding: 0.4rem 0.5rem !important;
            font-size: 0.85em !important;
            margin: auto;
            border-radius : 6px !important;
            transition : none;
        }
        .forum .composer__send .mdi {
            font-size: 1rem;
        }
        .composer__toolbar .composer__send .mdi {
            display : none !important; /*icone ➣ masquée sur mobile car trop petit*/
        }
    }`;
    document.head.appendChild(CSS);

    //LOAD_STORAGE_VALUE
    const loadSettings = JSON.parse(localStorage.getItem('risi-onche-storage') || '{}');

    //GLOBAL_VALUE
    const settings = {}; // { risiOverlay : ... , risiApi : ... , risiOpen : ... , replaceStickers : ... }
    settings.risiOverlay = loadSettings.risiOverlay ?? false;
    settings.risiApi = loadSettings.risiApi ?? false;
    settings.risiOpen = loadSettings.risiOpen ?? true;
    settings.replaceStickers = loadSettings.replaceStickers ?? false;

    let showNSFW = false;
    if (location.href.includes('onche.org/forum/3')) showNSFW = true;
    if (document.querySelector('.title a[href*="onche.org/forum/3"]')) showNSFW = true;

    //  AWAIT DOM TEXT PROMISE // POLL DOM IF IN JS (Retry 6 times)
    const textAreaSel = '.Form__input, #post textarea';
    let tentatives = 0;
    await new Promise(resolve => {
        (function check() {
            if (document.querySelector(textAreaSel)) return resolve();
            else if (++tentatives < 6) setTimeout(check, 300);
            else resolve(); // Fallback
        })();
    });

    //GLOBAL_NODE
    let placeChangeModAfter;
    let placeBtnAfter;
    let placeRisiAfter;
    let textArea = document.querySelector(textAreaSel);
    if (location.href.includes('onche.org/chat/')) { //MP CHAT
        placeChangeModAfter = document.querySelector('#right .bloc.border:last-of-type');
        placeBtnAfter = document.querySelector('.Messages');
        placeRisiAfter = document.querySelector('.Form.insert-image');
    } else {
        placeChangeModAfter = document.querySelector('#right .bloc.border:last-of-type');
        placeBtnAfter = document.querySelector('#post .composer__toolbar-items .onche-add, #post .composer__toolbar-items .item:last-child');
        placeRisiAfter = document.querySelector('#post .composer__stickers-host');
    }

    // LAPI a été mal compilée (MAJ 2025) : })(this.window = this.window || {});
    // INJECTION OBLIGATOIRE DE L'API DANS LE <HEAD> GLOBAL pour palier au soucis.
    // Sinon incompatibilité avec IOS / GREASYMONKEY ET VIOLENT MONKEY.
    const script = document.createElement('script');
    script.src = 'https://risibank.fr/downloads/web-api/risibank.js?v=1.3.2';
    document.head.appendChild(script);
    script.onload = () => risibankApiReady();

    // BRIDGE API IN HEAD FOR GREASYMONKEY
    const risiOnche = document.createElement('script');
    risiOnche.id = "risi-onche-bridge-api";
    risiOnche.textContent = `
    function activateRisibank(container, textArea, risiOverlay, showNSFW) {
        const themeRisi = document.body.matches('.blue, .grey') ? 'dark' : 'light';
        RisiBank.activate({
            type: risiOverlay ? 'overlay' : 'iframe',
            container,
            theme: themeRisi,
            mediaSize: 'sm',
            navbarSize: 'sm',
            defaultTab: 'fav',
            showNSFW: showNSFW,
            allowUsernameSelection: true,
            onSelectMedia: (e) => {
                RisiBank.Actions.addSourceImageLink(textArea)(e);
                textArea?.dispatchEvent(new Event('change', { bubbles: true })); /*Remonte un event pour refresh la preview*/
            }
        });
    }
    window.activateRisibank = activateRisibank;`;
    document.head.appendChild(risiOnche);

    // Appel direct de la fonction ou via unsafeWindow.fonction (TamperMonkey VS GreasyMonkey) - Grant None reste necessaire
    const activateRisibankSafe = typeof activateRisibank !== "undefined" ? activateRisibank : unsafeWindow.activateRisibank;


    // BTN OPEN RISIBANK
    function insertRisibankButton(placeBtnAfter, textArea, forceOverlay) {
        // BTN OPEN RISIBANK
        placeBtnAfter?.insertAdjacentHTML('afterend', `
            <div class="risibank-toggle item" title="Ouvrir / Fermer RisiBank">
               <img src="https://risibank.fr/logo.png" width="22" style="filter: hue-rotate(250deg)">
            </div>
        `);
        // BTN Listener
        placeBtnAfter?.parentElement.querySelector('.risibank-toggle')?.addEventListener('click', () => {
            //Bouton RisiOnche
            if (forceOverlay || settings.risiOverlay) {
                activateRisibankSafe(document.body, textArea, true, showNSFW);
            } else {
                settings.risiOpen = !settings.risiOpen; // Revert
                localStorage.setItem('risi-onche-storage', JSON.stringify(settings));

                embedRisiOnche(); //IFRAME
            }
        });
    }

    //Create IFRAME RisiOnche
    let risiOncheAlreadyMounted = false;
    function embedRisiOnche() {
        document.getElementById('risi-onche')?.classList.remove('risi-onche-open');
        if (settings.risiOverlay || !settings.risiOpen) return;

        document.getElementById('risi-onche')?.classList.add('risi-onche-open');

        if (!placeRisiAfter || risiOncheAlreadyMounted) return;

        //1ER INJECTION
        placeRisiAfter.insertAdjacentHTML('afterend', `<div id="risi-onche" class="risi-onche-open ${settings.replaceStickers ? 'risi-replace-stickers' : ''}" style="max-height: 30rem"></div>`);
        activateRisibankSafe(document.getElementById('risi-onche'), textArea, false, showNSFW);
        risiOncheAlreadyMounted = true
    }


    function risibankApiReady() {

        insertRisibankButton(placeBtnAfter, textArea, false); //BTN MAIN

        embedRisiOnche(); //MONTAGE IFRAME

        // Bloc Options dans la sidebar
        placeChangeModAfter?.insertAdjacentHTML('afterend', `
            <div id="risi-onche-panel" class="bloc border red">
                <div class="title">🔧 Risibank</div>
                <div class="content centered">
                    <button id="swtich-mode" class="button bordered secondary" type="button">Mode intégré : <span>${!settings.risiOverlay ? 'On' : 'Off'}</span></button>
                    <button id="replace-onche-stickers" class="button bordered secondary" type="button" title="Quand le panel s'ouvre, afficher l'intégration Risibank à la place des stickers onche (pour gagner de la place)">Masquer les stickers onche : <span>${settings.replaceStickers ? 'On' : 'Off'}</span></button>
                    <button id="open-risibank-url" class="button bordered secondary" type="button" title="Lors d'un clic sur un sticker noelshack, rediriger vers RisiBank si le sticker existe plutôt que noelshack">Rediriger vers RisiBank : <span>${settings.risiApi ? 'On' : 'Off'}</span></button>
                </div>
            </div>
        `);
        // Listeners Bloc Options
        document.getElementById('swtich-mode')?.addEventListener('click', (event) => {
            settings.risiOverlay = !settings.risiOverlay; // Revert
            event.currentTarget.querySelector('span').textContent = `${!settings.risiOverlay ? 'On' : 'Off'}`;
            localStorage.setItem('risi-onche-storage', JSON.stringify(settings));
            embedRisiOnche();
        });
        document.getElementById('replace-onche-stickers')?.addEventListener('click', (event) => {
            settings.replaceStickers = !settings.replaceStickers; // Revert
            document.getElementById('risi-onche')?.classList.toggle('risi-replace-stickers', settings.replaceStickers); // Revert Class
            event.currentTarget.querySelector('span').textContent = `${settings.replaceStickers ? 'On' : 'Off'}`;
            localStorage.setItem('risi-onche-storage', JSON.stringify(settings));
        });
        document.getElementById('open-risibank-url')?.addEventListener('click', (event) => {
            settings.risiApi = !settings.risiApi; // Revert
            event.currentTarget.querySelector('span').textContent = `${settings.risiApi ? 'On' : 'Off'}`;
            localStorage.setItem('risi-onche-storage', JSON.stringify(settings));
        });
    }

    // IMG_JVC => COPIE DU SRC DANS ALT pour pour copier coller avec SRC
    document.querySelectorAll('img[src^="https://image.noelshack.com/"]:not([data-processed-risionche])').forEach(imgShack => {
        imgShack.alt = imgShack.src;
        imgShack.setAttribute('data-processed-risionche', '');
    });

    // LIEN_RISI => Rempalce les liens RISIBANK par des IMAGES inline
    document.querySelectorAll('a.link[href^="https://risibank.fr/cache/medias/"]:not([data-processed-risionche])').forEach(linkRisi => {
        linkRisi.outerHTML = `
            <a href="${linkRisi.href}" class="link" data-processed-risionche target="_blank" rel="nofollow">
                <div class="smiley"><img src="${linkRisi.href}"></div>
            </a>
        `;
    });

    //GLOBAL LISTENER e.target
    document.body.addEventListener('click', async (e) => {
        // LIEN_JVC => OPEN API OR BYPASS 403
        const linkShack = e.target.closest('a.link[href^="https://image.noelshack.com/"]');
        if (linkShack) {
            if (settings.risiApi) {
                e.preventDefault();
                window.open('https://risibank.fr/api/v1/medias/by-source?type=jvc&url=' + linkShack.href, '_blank');
            } else if (!linkShack.matches('[rel~="noreferrer"]')) {
                e.preventDefault();
                window.open(linkShack.href, '_blank', 'noopener,noreferrer'); //FallBack NoReferrer If no balise REL
            }
        }
        //IMG_ONCHE => CONTEXTMENU => ADD OPTION : Voir en HD
        const imgOnche = e.target.closest('.logged .sticker > img[src^="https://cloud.onche.org/"]');
        if (imgOnche) {

            await new Promise(r => setTimeout(r, 100));

            const contextMenu = document.getElementById('context');
            const btnFullHDHere = document.getElementById('show-onche-hd');
            if (!contextMenu || btnFullHDHere) return;

            contextMenu.insertAdjacentHTML('beforeend', `
                <div class="item has-icon" id="show-onche-hd">
                    <span class="context-label">Voir en HD</span><span class="mdi mdi-printer-pos"></span>
                </div>`);
            document.getElementById('show-onche-hd').addEventListener('click', () => {
                window.open(imgOnche.src.split('/').slice(0, -1).join('/'), '_blank'); //Vire le l'index Ratio => Max Hd
            });
        }

        //BOUTON MODIFIER
        const btnItem = e.target.closest('#context .item.has-icon');
        if (btnItem?.querySelector('.mdi-pencil-outline')) { //Si bouton editer

            await new Promise(r => setTimeout(r, 300));

            //On traite tout les blocs ouvert en edition (plus simple) pour ajouter le BTN
            for (const editBlocMsg of document.querySelectorAll('.message.insert-image[data-edit]')) {

                if (editBlocMsg.querySelector('.risibank-toggle')) continue; //Si Btn deja ajouté on ignore

                const textAreaEdit = editBlocMsg.querySelector('textarea');
                const placeBtnAfterEdit = editBlocMsg.querySelector('.composer__toolbar .onche-add');
                insertRisibankButton(placeBtnAfterEdit, textAreaEdit, true); // ForceOverlay quand click en Mode Edition (trop compliqué iframe pour edition)
            }

        }

    }, true);

})();