MathsOnline ULTIMATE v2.6.2

Reads answers, injects SVG solutions (HIDDEN), shows in HUD only

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

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

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

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

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

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

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

Advertisement:

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

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

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

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

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

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

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

Advertisement:

// ==UserScript==
// @name         MathsOnline ULTIMATE v2.6.2
// @namespace    http://tampermonkey.net/
// @version      2.9
// @description  Reads answers, injects SVG solutions (HIDDEN), shows in HUD only
// @author       Phil 🥹👍 & DynaHacks
// @license      MIT
// @match        *://*.mathsonline.com.au/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // ---------- GLOBALS ----------
    let latestAnswers = [];
    let manualAnswers = [];
    let hudContainer = null;
    let hudContent = null;
    let isHidden = false;
    let progressData = { pointsCompleted: 0, pointsRequired: 0, totalCorrect: 0, totalIncorrect: 0 };
    let isWaitingForQuestion = true;

    let dynamicValues = {
        userQuestionSetId: null,
        worksheetId: null,
        taskId: null,
        menuId: null,
        diagnosticTypeId: null,
        tutorial: null,
        tutorialMaster: null,
        svgInteractiveId: null
    };

    // ---------- SVG INJECTOR FUNCTIONS ----------
    function logSVG(...args) {
        console.log('%c[SVG Injector]', 'color: #ff6b6b; font-weight: bold;', ...args);
    }

    const targetKeywords = [
        'getNextSvgInteractive?rid=',
        'startQuestionSet?rid='
    ];

    function isTargetURL(url) {
        return targetKeywords.some(k => url.includes(k));
    }

    function extractAnswersFromSVG(svgString) {
        const parser = new DOMParser();
        const svgDoc = parser.parseFromString(svgString, 'image/svg+xml');
        const answers = [];

        // Look for solution groups
        for (let i = 1; i <= 5; i++) {
            const sol = svgDoc.querySelector(`g.solution${i}`);
            if (sol) {
                const textEl = sol.querySelector('text');
                if (textEl && textEl.textContent) {
                    answers.push(textEl.textContent.trim());
                } else {
                    const title = sol.querySelector('title');
                    if (title && title.textContent) {
                        answers.push(title.textContent.trim());
                    }
                }
            }
        }

        // Check for answer markers
        const answerMarkers = svgDoc.querySelectorAll('[data-answer="true"], .answer, .correct');
        answerMarkers.forEach(marker => {
            const text = marker.textContent || marker.getAttribute('data-value');
            if (text) {
                answers.push(text.trim());
            }
        });

        return answers;
    }

    async function injectFromSVGString(svgString) {
        logSVG('Processing SVG for answers (hidden mode)');
        const parser = new DOMParser();
        const svgDoc = parser.parseFromString(svgString, 'image/svg+xml');

        // Extract answers from SVG and add to HUD
        const svgAnswers = extractAnswersFromSVG(svgString);
        if (svgAnswers.length > 0) {
            logSVG('Extracted answers from SVG (hidden):', svgAnswers);
            const combined = [...svgAnswers.map(a => ({ Answer: a })), ...manualAnswers];
            latestAnswers = combined;
            updateHUD(combined);
        }

        // Do NOT inject into the page — keep quiz clean
        logSVG('SVG solutions hidden (not injected into page)');
    }

    // ---------- STYLES ----------
    const styles = `
        #hacks-hud {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 340px;
            background: rgba(20, 20, 20, 0.95);
            color: #ffffff;
            border: 1px solid #00ff00;
            border-radius: 8px;
            font-family: monospace;
            box-shadow: 0 0 15px rgba(0, 255, 0, 0.2);
            z-index: 999999;
            transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
            user-select: none;
            font-size: 12px;
        }
        #hacks-hud-header {
            background: #111;
            padding: 8px 12px;
            font-weight: bold;
            font-size: 12px;
            border-bottom: 1px solid #333;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: grab;
        }
        #hacks-hud-header:active {
            cursor: grabbing;
        }
        #hacks-hud-title {
            color: #00ff00;
            font-size: 11px;
        }
        #hacks-hud-btn-group {
            display: flex;
            gap: 6px;
        }
        .hacks-hud-btn {
            background: #333;
            border: 1px solid #555;
            color: #aaa;
            padding: 2px 8px;
            border-radius: 3px;
            cursor: pointer;
            font-size: 10px;
            font-family: monospace;
        }
        .hacks-hud-btn:hover {
            background: #444;
            color: #fff;
        }
        #hacks-hud-spoof-btn {
            border-color: #00ff00;
            color: #00ff00;
        }
        #hacks-hud-spoof-btn:hover {
            background: #00ff00;
            color: #000;
        }
        #hacks-hud-body {
            padding: 10px 12px;
            max-height: 350px;
            overflow-y: auto;
            font-size: 11px;
        }
        .hacks-answer-row {
            display: flex;
            justify-content: space-between;
            padding: 4px 0;
            border-bottom: 1px solid #222;
        }
        .hacks-answer-row:last-child {
            border-bottom: none;
        }
        .hacks-label {
            color: #888;
        }
        .hacks-val {
            color: #00ff00;
            font-weight: bold;
        }
        .hacks-progress {
            background: #111;
            padding: 6px 10px;
            border-radius: 4px;
            margin-bottom: 8px;
            font-size: 11px;
            border-left: 2px solid #00ff00;
        }
        .hacks-progress-bar {
            background: #222;
            height: 4px;
            border-radius: 2px;
            margin-top: 4px;
            overflow: hidden;
        }
        .hacks-progress-bar-fill {
            height: 100%;
            background: #00ff00;
            border-radius: 2px;
            transition: width 0.3s ease;
        }
        .hacks-hidden-hud {
            opacity: 0 !important;
            pointer-events: none !important;
            transform: scale(0.95) translateY(-10px);
        }
        .hacks-progress-text {
            display: flex;
            justify-content: space-between;
            color: #aaa;
        }
        .hacks-progress-text span:first-child {
            color: #00ff00;
        }
        .hacks-progress-text span:last-child {
            color: #888;
        }
        .hacks-status-msg {
            color: #ffaa00;
            text-align: center;
            padding: 8px 0;
            font-size: 11px;
        }
        .hacks-status-msg.ready {
            color: #00ff00;
        }
        .hacks-section-title {
            color: #888;
            font-size: 10px;
            text-transform: uppercase;
            letter-spacing: 1px;
            margin: 8px 0 4px 0;
            border-bottom: 1px solid #222;
            padding-bottom: 2px;
        }
        .hacks-manual-input {
            display: flex;
            gap: 6px;
            margin-top: 8px;
            align-items: center;
        }
        .hacks-manual-input input {
            flex: 1;
            background: #222;
            border: 1px solid #444;
            color: #fff;
            padding: 4px 8px;
            border-radius: 3px;
            font-family: monospace;
            font-size: 11px;
        }
        .hacks-manual-input input:focus {
            outline: none;
            border-color: #00ff00;
        }
        .hacks-manual-input button {
            background: #333;
            border: 1px solid #555;
            color: #aaa;
            padding: 4px 10px;
            border-radius: 3px;
            cursor: pointer;
            font-size: 10px;
            font-family: monospace;
        }
        .hacks-manual-input button:hover {
            background: #444;
            color: #fff;
        }
        .hacks-manual-list {
            margin-top: 6px;
            font-size: 10px;
            color: #888;
        }
        .hacks-manual-list span {
            color: #00ff00;
        }
    `;

    function injectStyles() {
        if (document.getElementById('hacks-styles')) return;
        const styleSheet = document.createElement('style');
        styleSheet.id = 'hacks-styles';
        styleSheet.innerText = styles;
        document.head.appendChild(styleSheet);
    }

    // ---------- DETECTION FUNCTIONS ----------
    function getTutorialFromPage() {
        const urlParams = new URLSearchParams(window.location.search);
        let tutorial = urlParams.get('tutorial') || urlParams.get('tutorialId');

        if (!tutorial) {
            const pathParts = window.location.pathname.split('/');
            for (let i = 0; i < pathParts.length; i++) {
                if (pathParts[i] === 'lessons' && pathParts[i+1]) {
                    tutorial = pathParts[i+2] || pathParts[i+1];
                    break;
                }
            }
        }

        if (!tutorial && typeof window.tutorial !== 'undefined') {
            tutorial = window.tutorial;
        }
        if (!tutorial && window.currentQuestionSet) {
            tutorial = window.currentQuestionSet.tutorial;
        }

        return tutorial || null;
    }

    function getTutorialMasterFromPage() {
        const urlParams = new URLSearchParams(window.location.search);
        let tutorialMaster = urlParams.get('tutorialMaster') || urlParams.get('tutorialmaster');

        if (!tutorialMaster && typeof window.tutorialMaster !== 'undefined') {
            tutorialMaster = window.tutorialMaster;
        }
        if (!tutorialMaster && window.currentQuestionSet) {
            tutorialMaster = window.currentQuestionSet.tutorialMaster;
        }

        if (!tutorialMaster) {
            for (let key in window) {
                if (key.toLowerCase().includes('tutorialmaster') || key.toLowerCase().includes('tutorial_master')) {
                    const val = window[key];
                    if (typeof val === 'string' && /^\d+$/.test(val)) {
                        tutorialMaster = val;
                        break;
                    }
                }
            }
        }

        return tutorialMaster || null;
    }

    // ---------- HUD ----------
    function createHUD() {
        if (document.getElementById('hacks-hud')) return;

        hudContainer = document.createElement('div');
        hudContainer.id = 'hacks-hud';

        hudContainer.innerHTML = `
            <div id="hacks-hud-header">
                <span id="hacks-hud-title">MathsOnline ULTIMATE v2.6.2</span>
                <div id="hacks-hud-btn-group">
                    <button class="hacks-hud-btn" id="hacks-hud-spoof-btn">SPOOF</button>
                    <button class="hacks-hud-btn" id="hacks-hud-toggle-btn">HIDE</button>
                </div>
            </div>
            <div id="hacks-hud-body">
                <div id="hacks-progress-container" style="display:none;"></div>
                <div id="hacks-status-container">
                    <div class="hacks-status-msg">Waiting for question...</div>
                </div>
                <div id="hacks-answers-container"></div>
                <div id="hacks-manual-container" style="display:none;">
                    <div class="hacks-section-title">Manual Answer</div>
                    <div class="hacks-manual-input">
                        <input type="text" id="hacks-manual-input" placeholder="Enter answer (comma separated for multiple)">
                        <button id="hacks-manual-add-btn">Add</button>
                    </div>
                    <div id="hacks-manual-list" class="hacks-manual-list">Manual answers: <span>none</span></div>
                </div>
            </div>
        `;

        document.documentElement.appendChild(hudContainer);
        hudContent = document.getElementById('hacks-hud-body');

        document.getElementById('hacks-hud-toggle-btn').addEventListener('click', toggleHUD);
        document.getElementById('hacks-hud-spoof-btn').addEventListener('click', forceSpoof);

        document.getElementById('hacks-manual-add-btn').addEventListener('click', addManualAnswer);
        document.getElementById('hacks-manual-input').addEventListener('keydown', (e) => {
            if (e.key === 'Enter') addManualAnswer();
        });

        makeDraggable(hudContainer);
        console.log('[HUD] Created v2.6.2');
    }

    function addManualAnswer() {
        const input = document.getElementById('hacks-manual-input');
        const value = input.value.trim();
        if (!value) return;

        const answers = value.split(',').map(s => s.trim()).filter(s => s);
        answers.forEach(ans => {
            manualAnswers.push({ Answer: ans });
        });

        input.value = '';
        updateManualDisplay();
        updateCombinedAnswers();
    }

    function updateManualDisplay() {
        const list = document.getElementById('hacks-manual-list');
        if (!list) return;
        if (manualAnswers.length === 0) {
            list.innerHTML = 'Manual answers: <span>none</span>';
        } else {
            list.innerHTML = 'Manual answers: <span>' + manualAnswers.map(a => a.Answer).join(', ') + '</span>';
        }
    }

    function updateCombinedAnswers() {
        const combined = [...latestAnswers, ...manualAnswers];
        if (combined.length > 0) {
            updateStatus('Ready to spoof (' + combined.length + ' answers)', true);
        }
        renderAnswers(combined);
    }

    function renderAnswers(answers) {
        const answersContainer = document.getElementById('hacks-answers-container');
        if (!answersContainer) return;

        if (!answers || answers.length === 0) {
            answersContainer.innerHTML = '';
            return;
        }

        let html = '<div class="hacks-section-title">Answers</div>';
        answers.forEach((item, idx) => {
            html += `
                <div class="hacks-answer-row">
                    <span class="hacks-label">Q${idx+1}:</span>
                    <span class="hacks-val">${item.Answer}</span>
                </div>
            `;
        });
        html += `<div style="margin-top:6px;text-align:right;font-size:10px;color:#555;">${answers.length} answer(s) ready</div>`;
        answersContainer.innerHTML = html;
    }

    function ensureHUD() {
        if (!document.getElementById('hacks-hud')) {
            console.log('[HUD] Re-injecting...');
            createHUD();
            if (isHidden && hudContainer) {
                hudContainer.classList.add('hacks-hidden-hud');
            }
        }
    }

    const observer = new MutationObserver(() => ensureHUD());
    observer.observe(document.documentElement, { childList: true, subtree: true });
    setInterval(ensureHUD, 3000);

    function makeDraggable(element) {
        const header = document.getElementById('hacks-hud-header');
        if (!header) return;
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        header.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            if (e.target.classList && e.target.classList.contains('hacks-hud-btn')) return;
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            element.style.top = (element.offsetTop - pos2) + "px";
            element.style.left = (element.offsetLeft - pos1) + "px";
            element.style.right = "auto";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }

    function toggleHUD() {
        isHidden = !isHidden;
        if (hudContainer) {
            hudContainer.classList.toggle('hacks-hidden-hud', isHidden);
        }
    }

    window.addEventListener('keydown', (e) => {
        if (e.key.toLowerCase() === 'h' && !e.ctrlKey && !e.metaKey) {
            toggleHUD();
        }
    });

    function updateProgress(pointsCompleted, pointsRequired, totalCorrect, totalIncorrect) {
        const progressContainer = document.getElementById('hacks-progress-container');
        if (!progressContainer) return;

        if (pointsRequired > 0) {
            progressContainer.style.display = 'block';
            const percent = Math.min((pointsCompleted / pointsRequired) * 100, 100);
            progressContainer.innerHTML = `
                <div class="hacks-progress">
                    <div class="hacks-progress-text">
                        <span>${pointsCompleted}</span>
                        <span>/${pointsRequired} points</span>
                        <span style="color:#888;"> ${totalCorrect} | ${totalIncorrect}</span>
                    </div>
                    <div class="hacks-progress-bar">
                        <div class="hacks-progress-bar-fill" style="width:${percent}%;"></div>
                    </div>
                </div>
            `;
        } else {
            progressContainer.style.display = 'none';
        }
    }

    function updateStatus(message, isReady) {
        const statusContainer = document.getElementById('hacks-status-container');
        if (!statusContainer) return;
        const className = isReady ? 'hacks-status-msg ready' : 'hacks-status-msg';
        statusContainer.innerHTML = `<div class="${className}">${message}</div>`;
        isWaitingForQuestion = !isReady;
    }

    function updateHUD(answers) {
        if (!hudContent) return;

        const answersContainer = document.getElementById('hacks-answers-container');
        if (!answersContainer) return;

        if ((!answers || answers.length === 0) && manualAnswers.length === 0) {
            answersContainer.innerHTML = '';
            updateStatus('Waiting for question...', false);
            document.getElementById('hacks-manual-container').style.display = 'block';
            return;
        }

        const combined = [...(answers || []), ...manualAnswers];
        latestAnswers = combined;
        renderAnswers(combined);
        updateStatus('Ready to spoof (' + combined.length + ' answers)', true);
        document.getElementById('hacks-manual-container').style.display = 'block';
    }

    // ---------- ANSWER INTERCEPTOR ----------
    function analyzeAndProcess(rawText) {
        const trimmed = rawText.trim();
        if (trimmed.startsWith('{') && (trimmed.includes('"components"') || trimmed.includes('"userQuestionSetId"'))) {
            try {
                const data = JSON.parse(trimmed);

                if (data.userQuestionSetId) {
                    dynamicValues.userQuestionSetId = data.userQuestionSetId;
                }
                if (data.worksheetId) dynamicValues.worksheetId = data.worksheetId;
                if (data.taskId) dynamicValues.taskId = data.taskId;
                if (data.menuId) dynamicValues.menuId = data.menuId;
                if (data.diagnosticTypeId) dynamicValues.diagnosticTypeId = data.diagnosticTypeId;
                if (data.tutorial) dynamicValues.tutorial = data.tutorial;
                if (data.tutorialMaster) dynamicValues.tutorialMaster = data.tutorialMaster;
                if (data.svgInteractiveId) dynamicValues.svgInteractiveId = data.svgInteractiveId;

                if (data.activeTutorial) {
                    dynamicValues.tutorial = data.activeTutorial;
                    dynamicValues.tutorialMaster = data.activeTutorial;
                }

                if (data.pointsCompleted !== undefined) {
                    progressData.pointsCompleted = data.pointsCompleted;
                    progressData.pointsRequired = data.pointsRequired || 0;
                    progressData.totalCorrect = data.totalCorrect || 0;
                    progressData.totalIncorrect = data.totalIncorrect || 0;
                    updateProgress(
                        progressData.pointsCompleted,
                        progressData.pointsRequired,
                        progressData.totalCorrect,
                        progressData.totalIncorrect
                    );
                }

                const answersFound = [];

                if (data.marks && Array.isArray(data.marks) && data.marks.length > 0) {
                    const markAnswers = data.marks.map(m => m.correct).join(', ');
                    answersFound.push({
                        ComponentID: "marks",
                        Answer: markAnswers
                    });
                }

                if (data && data.components) {
                    data.components.forEach(comp => {
                        if (comp.ComponentTypeID === "2" && comp.Data) {
                            try {
                                const parsedData = JSON.parse(comp.Data);
                                if (parsedData.Answers) {
                                    answersFound.push({
                                        ComponentID: comp.ComponentID,
                                        Answer: parsedData.Answers
                                    });
                                }
                            } catch (innerErr) {}
                        }
                    });
                }

                if (answersFound.length > 0) {
                    const combined = [...answersFound, ...manualAnswers];
                    latestAnswers = combined;
                    updateHUD(combined);
                } else {
                    updateStatus('No auto-detected answers. Use manual input.', false);
                    document.getElementById('hacks-manual-container').style.display = 'block';
                }
            } catch (e) {}
        }
    }

    // ---------- FETCH + XHR HOOKS ----------
    const originalFetch = window.fetch;
    window.fetch = async function(resource, ...args) {
        const url = typeof resource === 'string' ? resource : resource.url;
        const response = await originalFetch.call(this, resource, ...args);

        try {
            const clone = response.clone();
            const text = await clone.text();

            if (url && isTargetURL(url)) {
                try {
                    const json = JSON.parse(text);
                    if (json.svg) {
                        logSVG('SVG detected in fetch (hidden mode)');
                        const svgAnswers = extractAnswersFromSVG(json.svg);
                        if (svgAnswers.length > 0) {
                            logSVG('Extracted answers from SVG (hidden):', svgAnswers);
                            const combined = [...svgAnswers.map(a => ({ Answer: a })), ...manualAnswers];
                            latestAnswers = combined;
                            updateHUD(combined);
                        }
                        // Hidden mode: do NOT inject into page
                    }
                } catch (e) {}
            }

            analyzeAndProcess(text);
        } catch (e) {}

        return response;
    };

    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url) {
        this._url = url;
        return originalOpen.apply(this, arguments);
    };

    const originalSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.send = function() {
        this.addEventListener('load', () => {
            if (this._url && isTargetURL(this._url)) {
                try {
                    const json = JSON.parse(this.responseText);
                    if (json.svg) {
                        logSVG('SVG detected in XHR (hidden mode)');
                        const svgAnswers = extractAnswersFromSVG(json.svg);
                        if (svgAnswers.length > 0) {
                            logSVG('Extracted answers from SVG (hidden):', svgAnswers);
                            const combined = [...svgAnswers.map(a => ({ Answer: a })), ...manualAnswers];
                            latestAnswers = combined;
                            updateHUD(combined);
                        }
                        // Hidden mode: do NOT inject into page
                    }
                } catch (e) {}
            }

            try {
                analyzeAndProcess(this.responseText);
            } catch (e) {}
        });

        return originalSend.apply(this, arguments);
    };

    // ---------- FORCE SPOOF ----------
    async function forceSpoof() {
        const combined = [...latestAnswers, ...manualAnswers];
        const useAnswers = combined.length > 0;

        const tutorial = dynamicValues.tutorial || getTutorialFromPage();
        const tutorialMaster = dynamicValues.tutorialMaster || getTutorialMasterFromPage();

        if (!tutorial || !tutorialMaster) {
            alert('Could not detect tutorial or tutorialMaster.\nMake sure you are inside an active lesson.');
            return;
        }

        try {
            const startRes = await fetch(
                `https://www.mathsonline.com.au/ajax/svg_interactives/startQuestionSet?rid=${Math.random()}`,
                {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'X-Requested-With': 'XMLHttpRequest'
                    },
                    body: new URLSearchParams({
                        userQuestionSetTypeId: '7',
                        tutorial: tutorial,
                        tutorialMaster: tutorialMaster,
                        menuId: '102',
                        worksheetId: '0',
                        taskId: '0',
                        diagnosticTypeId: '0',
                    })
                }
            );
            const startData = await startRes.json();
            const userQuestionSetId = startData.userQuestionSetId;
            const svgInteractiveId = startData.svgInteractiveId;

            let inputData;
            if (useAnswers) {
                inputData = [{
                    questionId: 1,
                    inputs: combined.map((item, idx) => ({
                        id: `input${idx+1}`,
                        value: item.Answer,
                        correct: "true"
                    })),
                    checkboxes: [],
                    radiobuttons: [],
                    selectables: [],
                    draggables: [],
                    marks: combined.map((item, idx) => ({
                        id: `mark${idx+1}`,
                        correct: "true"
                    })),
                    solutions: []
                }];
            } else {
                inputData = [{
                    questionId: 1,
                    inputs: [{ id: "input1", value: "1", correct: "true" }],
                    checkboxes: [],
                    radiobuttons: [],
                    selectables: [],
                    draggables: [],
                    marks: [{ id: "mark1", correct: "true" }],
                    solutions: []
                }];
            }

            const payload = {
                userQuestionSetId: userQuestionSetId,
                svgInteractiveId: svgInteractiveId,
                tutorial: tutorial,
                tutorialMaster: tutorialMaster,
                menuId: '102',
                worksheetId: '0',
                diagnosticTypeId: '0',
                userQuestionSetTypeId: '7',
                difficulty: '1',
                correct: useAnswers ? combined.length : 1,
                incorrect: 0,
                duration: Math.floor(8000 + Math.random() * 15000),
                data: JSON.stringify(inputData)
            };

            const submitRes = await fetch(
                `https://www.mathsonline.com.au/ajax/svg_interactives/submitResult?r=${Math.random()}`,
                {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'X-Requested-With': 'XMLHttpRequest'
                    },
                    body: new URLSearchParams(payload)
                }
            );

            const rawText = await submitRes.text();

            try {
                const result = JSON.parse(rawText);
                if (result.pointsCompleted !== undefined) {
                    progressData.pointsCompleted = result.pointsCompleted;
                    progressData.pointsRequired = result.pointsRequired || 0;
                    progressData.totalCorrect = result.totalCorrect || 0;
                    progressData.totalIncorrect = result.totalIncorrect || 0;
                    updateProgress(
                        progressData.pointsCompleted,
                        progressData.pointsRequired,
                        progressData.totalCorrect,
                        progressData.totalIncorrect
                    );
                }

                alert('Submitted ' + (useAnswers ? combined.length : 1) + ' answer(s)! Points: ' + progressData.pointsCompleted + '/' + progressData.pointsRequired);
                setTimeout(() => window.location.reload(), 800);
            } catch (parseError) {
                alert('Server error: ' + rawText.substring(0, 100));
            }
        } catch (e) {
            console.error('[SPOOF] Submission failed:', e);
            alert('Spoof failed - check console.');
        }
    }

    // ---------- INIT ----------
    window.addEventListener('DOMContentLoaded', () => {
        injectStyles();
        createHUD();
        console.log('[INIT] Tutorial:', getTutorialFromPage(), 'Master:', getTutorialMasterFromPage());
        updateStatus('Waiting for question...', false);
        document.getElementById('hacks-manual-container').style.display = 'block';
    });

    logSVG('SVG Injector merged (hidden mode)');
    console.log('%c[MathsOnline ULTIMATE] Loaded v2.6.2', 'color: #00ff00; font-weight: bold;');
})();