Reads answers, injects SVG solutions (HIDDEN), shows in HUD only
// ==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;');
})();