Greasy Fork is available in English.

Testout Retry

Remove the Lab Score Report window on testout labs to fix what you missed and quickly resubmit.

// ==UserScript==
// @name         Testout Retry
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Remove the Lab Score Report window on testout labs to fix what you missed and quickly resubmit.
// @author       Ultimate
// @match        https://cdn.testout.com/*
// ==/UserScript==

let scoreReportOpen = false;
let lastRetryTime = -1;
let retry = 0;
const prefix = '[Retry] ';
log('INJECTED INTO IFRAME');

//Set interval for checking if lab report is open
setInterval(() => {
    let pre = scoreReportOpen;
    const doneBtn = getDoneBtn();
    scoreReportOpen = doneBtn !== null;
    if (pre != scoreReportOpen) onLabScoreOpened();
}, 100);

function onLabScoreOpened() {
    if (Date.now() - lastRetryTime < 500) return;
    log('SCORE WINDOW OPENED');
    const doneBtn = getDoneBtn();

    if (doneBtn == null || doneBtn == undefined) return;

    //Add our own button
    const retryBtn = document.createElement('button');
    retryBtn.innerText = 'Retry Lab';
    retryBtn.style = 'position: absolute; z-index: 99999; overflow: visible; color: #dd2575; display: inherit; left: 1135px; top: 793px; border-width: 0px; width: 31px; height: 14px; text-align: left; font-family: Arial; font-weight: bold; font-size: 9pt; text-overflow: clip; overflow-wrap: normal; white-space: nowrap;';

    retryBtn.addEventListener('click', () => {
        log('REMOVING LAB SCORE WINDOW..');
        lastRetryTime = Date.now();

        const removeElements = [
            'ScoreReportWindow.Grid',
            'bWindow',
            'DockPanel.Border',
            'DockPanel.Border.Panel.tbTitle',
            'Background',
            'Background.Grid',
            'BackgroundGradient',
            'bPrint.Grid.contentPresenter.Image',
            'DockPanel.Border1',
            'DockPanel.Border1.StackPanel.Border',
            'svResults',
            'pnConnecting.TextBlock',
            'btDone.Grid.Border',
            'btDone.Grid.Border.NormalElement',
            'btDone.Grid.Border.NormalElement.Border',
            'btDone.Grid.contentPresenter.TextBlock',
            'UIRoot.ScoreReportWindow.Grid.bWindow.DockPanel.Border1.StackPanel.Panel.btDone.Grid.Border.NormalElement.Border',
            'UIRoot.ScoreReportWindow.Grid.bWindow.DockPanel.Border1.StackPanel.Panel.btDone.Grid.MouseOverElement',
            '_ifrmreport_',
            'bWindow.DockPanel'
        ];

        retryBtn.remove();

        let removed = 0;

        //Method 1
        removeElements.forEach(id => {
            let el = document.querySelector('#'+id.replace(/\./g, '\\.')); //.s replaced with \\. so it doesnt think its a class
            if (el !== null) {
                el.remove();
                removed++;
            }
        });

        //Method 2 - Loop through all elements because testout changes the names each score for some reason
        const all = document.querySelectorAll('.clsFrameworkElement');
        for (let i = 1; i < all.length; i++) {
            if (all[i].style.zIndex >= 99999 || all[i].id.includes('ScoreReport') || all[i].id.includes('bWindow.DockPanel')) {
                all[i].remove();
                removed++;
            }
        }

        log(`Successfully removed ${removed} elements and destroyed score report window.`); //Done!
        retry++;
    });

    if (doneBtn !== null) doneBtn.parentNode.appendChild(retryBtn);
}

//For some reason on rescore the id is different
function getDoneBtn() {
   const all = document.querySelectorAll('.clsFrameworkElement');

    for (let i = 1; i < all.length; i++) {
        if (all[i].id.includes('Done') && all[i].innerText === 'Done' && all[i].style.display === 'inherit') {
            return all[i];
        }
    }

    return null;
}

function log(msg) {
    console.log(prefix+msg);
}