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