YNO game popout

adds a pop out button to any yno game

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         YNO game popout
// @namespace    https://kutt.it/meqa
// @version      0.8
// @description  adds a pop out button to any yno game
// @author       meqativ
// @match        https://ynoproject.net/*/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ynoproject.net
// @grant        GM_registerMenuCommand
// @run-at       document-idle
// @license MIT 
// ==/UserScript==
function getElsFromString(html) {
    const temp = document.createElement("div");
    temp.innerHTML = html;
    const out = temp.children;
    delete temp;
    return out
}
const [filler, btn] = getElsFromString(`<div id="_filler">
                                            <p style="color: #fff; height: min-content;">Popped out<br/>(press this or close the window to pop back in)</p>
                                        </div>
                                        <button id="popoutButton" class="iconButton">
                                            <svg fill="none" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 18 18">
                                                <path d="M-4,2.5 v13 h18 v-13 h-18 z M1,7 l3.5,3.5 m-3.5,0 l3.5,0 m0,-3.5 l0,3.5"></path>
                                                <path style="fill:var(--svg-base-gradient);" d="M6.5,12.5 v5.2 h9 v-5.2 h-9 z"></path>
                                            </svg>
                                        </button>`)
filler.style = `background-color: rgba(0, 0, 0, 0.15);
                display: flex;
                aspect-ratio: 320/240;
                width: 100%;
                backdrop-filter: blur(4px);
                justify-content: center;
                align-items: center;
                border: 2px solid black;`;
function exec(rerun) {
    console.log("hiii!!!!!")
    const controlsFullscreen = document.getElementById("controls-fullscreen");
    if (!controlsFullscreen) throw `Not a game idk`
    const rightControls = controlsFullscreen.parentNode;
    if (!document.getElementById('popoutButton')) {
        rightControls.insertBefore(btn, controlsFullscreen)
    } else {
        let btn_old = document.getElementById('popoutButton');
        btn_old.parentNode.replaceChild(btn, btn_old);
    }
    btn.addEventListener("click", run);

    let tooltipped = false; // stupid because addTooltip isn't defined yet
    function tool_tip() {
        if (tooltipped) {
            btn.removeEventListener("focus", tool_tip);
            btn.removeEventListener("mouseover", tool_tip);
            return;
        }
        tooltipped = true;
        addTooltip(btn, "Pop out", true, true)

    }
    btn.addEventListener("focus", tool_tip);
    btn.addEventListener("mouseover", tool_tip);
    function run() {
        const player = document.querySelector("#canvas");
        let _filler = document.querySelector("#canvasContainer #_filler");
        if (!player && _filler) return _filler.click();
        btn.removeEventListener("click", run);
        btn.style.setProperty("display", "none");
        let current = player;
        const originalGetElementById = document.getElementById;

        document.getElementById = function() {
            if (arguments[0] === "canvas") return current;
            return originalGetElementById.call(document, ...arguments);
        };


        const pos = player.getBoundingClientRect();
        const ctx = open("", window.location.href, `popup,innerHeight=${pos.height},innerWidth=${pos.width},screenX=${pos.x+window.screenX},screenY=${pos.y+window.screenY}`);
        if (ctx) {
            ctx.document.head.innerHTML = `<style> * {
    margin: 0;
    padding: 0;
}
:root {
    background-color: #000;
}

#canvas {
  display: block;
  object-fit: contain;
  min-width: 0px;
  min-height: 0px;
  aspect-ratio: 320 / 240;
  height: 100%;
  width: 100%;
  image-rendering: pixelated;
} </style>`;
            const title = document.createElement("title");
            title.innerHTML = `Loading... - ${window.location.pathname}`;
            ctx.document.head.appendChild(title);

            const connStatusText = document.querySelector("#connStatusText")
            const locationDisplayLabel = document.querySelector("#locationDisplayLabel");
            const currentTitle = {
                game: window.location.pathname.split("/")[1],
                location: locationDisplayLabel.innerHTML,
                connectionStatus: connStatusText.innerHTML,
            }
            function updateTitle() {
                const { location, game, connectionStatus } = currentTitle;
                title.innerText = [
                    (connectionStatus === "Connected") ? undefined : connectionStatus,
                    location,
                    game
                ].filter(Boolean).join(" | ");
            }

            updateTitle();
            const observer = new MutationObserver((mutations, observer) => {
                for (const mutation of mutations) {
                    if (mutation.target === connStatusText) currentTitle.connectionStatus = mutation.addedNodes?.[0]?.data;
                    else if (mutation.target === locationDisplayLabel) currentTitle.location = mutation.addedNodes?.[0]?.data;
                    updateTitle();
                }
            });
            observer.observe(locationDisplayLabel, { childList: true });
            observer.observe(connStatusText, { childList: true });

            const parent = player.parentNode;
            parent.removeChild(player);
            parent.appendChild(filler);

            current = filler;
            let poppedin = false;
            function popin(closing) {
                if (closing) {
                    window.removeEventListener("beforeunload", popin)
                    btn.addEventListener("click", run);
                    btn.style.setProperty("display", "");
                }
                if (!poppedin){
                    clearInterval(currentTitle.intervalId)
                    filler.removeEventListener("click", popin);
                    parent.removeChild(filler);
                    player.parentNode.removeChild(player);
                    parent.appendChild(player);
                    current = player;
                    poppedin = true
                    return ctx.close();
                }
            }
            filler.addEventListener("click", popin);
            window.addEventListener("beforeunload", popin)
            ctx.addEventListener("beforeunload", () => popin(true))
            ctx.document.body.appendChild(player);
        } else { alert("Allow popups!!"); }
    }
    if (rerun === true) console.info("reloaded")
}
GM_registerMenuCommand('reload', () => exec(true))
exec()