Fixed manual entry to clear both inputs after setting
// ==UserScript==
// @name AT Tracker
// @namespace http://tampermonkey.net/
// @version 9.4
// @description Fixed manual entry to clear both inputs after setting
// @author Daniel Wu
// @match https://www.pottersschool.org/student/
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- Core State ---
const STORAGE_KEY = 'AT_V9_DATA';
const CONFIG_KEY = 'AT_V9_CONFIG';
const ACCENT = '#00f0ff'; // Cyber Cyan
let answers = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
let config = JSON.parse(localStorage.getItem(CONFIG_KEY) || '{"x": 60, "y": 120, "isMaximized": true, "autoCapture": true}');
// --- Shadow DOM Setup (The Ultimate Isolation) ---
const host = document.createElement('div');
host.id = 'at-tracker-host';
document.body.appendChild(host);
const shadow = host.attachShadow({mode: 'open'});
const container = document.createElement('div');
container.id = 'at-main-container';
shadow.appendChild(container);
// --- Luxury Cyber UI Styles ---
const style = document.createElement('style');
style.textContent = `
:host {
all: initial;
display: block;
}
#at-main-container {
position: fixed;
z-index: 2147483647;
left: ${config.x}px;
top: ${config.y}px;
width: 280px;
background: #000000;
backdrop-filter: blur(25px);
-webkit-backdrop-filter: blur(25px);
border-radius: 12px;
color: #ffffff;
font-family: 'Inter', -apple-system, system-ui, sans-serif;
overflow: hidden;
box-sizing: border-box;
/* Chromatic Aberration & Glow Border */
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow:
-1.5px 0 0 rgba(0, 240, 255, 0.15),
1.5px 0 0 rgba(255, 0, 80, 0.05),
0 30px 60px rgba(0, 0, 0, 0.8);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
user-select: none;
}
#at-main-container:hover {
transform: translateY(-4px) scale(1.02);
box-shadow: 0 40px 80px rgba(0, 0, 0, 0.9);
}
/* Typography & Layout */
.header {
padding: 18px 24px;
background: #050505;
cursor: move;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}
.title {
font-size: 11px;
font-weight: 800;
letter-spacing: 2px;
color: rgba(255, 255, 255, 0.7);
text-transform: uppercase;
}
.body {
padding: 24px;
display: ${config.isMaximized ? 'block' : 'none'};
}
.list {
max-height: 250px;
overflow-y: auto;
margin-bottom: 24px;
}
.list::-webkit-scrollbar { width: 0px; }
.item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.02);
line-height: 1.6;
}
.q-tag {
font-size: 11px;
font-weight: 700;
color: rgba(255, 255, 255, 0.6);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.ans-val {
font-size: 16px;
font-weight: 900;
color: ${ACCENT};
text-shadow: 0 0 12px rgba(0, 240, 255, 0.4);
}
/* Form Controls */
.form {
display: flex;
gap: 12px;
width: 100%;
}
.input {
flex: 1;
min-width: 0;
background: #0a0a0a;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
padding: 10px 12px;
font-size: 13px;
color: #ffffff;
transition: border 0.3s;
}
.input:focus { border-color: ${ACCENT}; }
.btn {
background: #ffffff;
color: #000000;
border: none;
border-radius: 6px;
padding: 0 16px;
font-size: 10px;
font-weight: 900;
cursor: pointer;
text-transform: uppercase;
display: flex;
align-items: center;
justify-content: center;
height: 38px;
transition: all 0.2s;
}
.btn:hover { background: #e2e8f0; transform: translateY(-1px); }
/* Footer */
.footer {
margin-top: 24px;
padding-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.03);
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.status {
font-size: 9px;
font-weight: 800;
letter-spacing: 1px;
cursor: pointer;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.2);
}
.status.active { color: ${ACCENT}; }
.footer-actions {
display: flex;
gap: 12px;
}
.action-link {
font-size: 9px;
font-weight: 800;
color: #ffffff;
cursor: pointer;
text-transform: uppercase;
transition: color 0.2s;
}
.action-link:hover { color: ${ACCENT}; }
.action-link.danger:hover { color: #f43f5e; }
.mini-dot {
width: 44px; height: 44px;
display: flex; align-items: center; justify-content: center;
cursor: move;
}
.dot { width: 8px; height: 8px; background: ${ACCENT}; border-radius: 50%; box-shadow: 0 0 15px ${ACCENT}; }
`;
shadow.appendChild(style);
// --- UI Logic ---
function updateStructure() {
if (!config.isMaximized) {
container.style.width = '44px';
container.innerHTML = `<div class="mini-dot" id="at-header-drag"><div class="dot" id="at-toggle"></div></div>`;
} else {
container.style.width = '280px';
container.innerHTML = `
<div class="header" id="at-header-drag">
<span class="title">AT TRACKER</span>
<span id="at-toggle" style="cursor:pointer; color:#ffffff; font-size:16px;">—</span>
</div>
<div class="body">
<div class="list" id="at-list"></div>
<div class="form">
<input class="input" id="in-q" type="number" placeholder="ID">
<input class="input" id="in-a" type="text" placeholder="ANS">
<button class="btn" id="btn-save">SET</button>
</div>
<div class="footer">
<span id="sync-toggle" class="status ${config.autoCapture ? 'active' : ''}">AUTO TRACK</span>
<div class="footer-actions">
<span id="btn-export" class="action-link">EXPORT</span>
<span id="btn-clear" class="action-link danger">CLEAR ALL</span>
</div>
</div>
</div>
`;
shadow.getElementById('btn-save').onclick = () => {
const qIn = shadow.getElementById('in-q');
const aIn = shadow.getElementById('in-a');
const q = qIn.value;
const a = aIn.value;
if (q && a) {
saveAnswer(q, a);
qIn.value = ''; // Clear ID
aIn.value = ''; // Clear Answer
}
};
shadow.getElementById('sync-toggle').onclick = () => {
config.autoCapture = !config.autoCapture;
saveConfig();
updateStructure();
renderList();
};
shadow.getElementById('btn-export').onclick = () => {
exportData();
};
shadow.getElementById('btn-clear').onclick = () => {
if(confirm('PURGE ALL SESSION DATA?')) { answers = {}; renderList(); }
};
}
shadow.getElementById('at-toggle').onclick = (e) => {
e.stopPropagation();
config.isMaximized = !config.isMaximized;
saveConfig();
updateStructure();
renderList();
};
initDrag(shadow.getElementById('at-header-drag'));
}
function renderList() {
const list = shadow.getElementById('at-list');
if (!list) return;
list.innerHTML = '';
const keys = Object.keys(answers).sort((a, b) => parseInt(a) - parseInt(b));
if (keys.length === 0) {
list.innerHTML = '<div style="text-align:center; color:rgba(255,255,255,0.03); font-size:8px; padding:40px; letter-spacing:4px; font-weight:800;">EMPTY_CORE</div>';
} else {
keys.forEach(k => {
const div = document.createElement('div');
div.className = 'item';
div.innerHTML = `
<span class="q-tag">Q${k}</span>
<span class="ans-val">${answers[k]}</span>
<span style="cursor:pointer;color:rgba(255,255,255,0.1);font-size:12px;" onclick="window.atDel('${k}')">×</span>
`;
list.appendChild(div);
});
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(answers));
list.scrollTop = list.scrollHeight;
}
function exportData() {
const keys = Object.keys(answers).sort((a, b) => parseInt(a) - parseInt(b));
if (keys.length === 0) return;
let content = "AT TRACKER EXPORT\n";
content += "Generated: " + new Date().toLocaleString() + "\n";
content += "--------------------------\n\n";
keys.forEach(k => {
content += `Q${k}: ${answers[k]}\n`;
});
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `at_tracker_export_${Date.now()}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
window.atDel = (id) => { delete answers[id]; renderList(); };
function saveAnswer(q, a) {
answers[q] = a.toUpperCase();
renderList();
}
// --- Drag Engine ---
function initDrag(handle) {
let p1 = 0, p2 = 0, p3 = 0, p4 = 0;
handle.onmousedown = (e) => {
if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT') return;
e.preventDefault();
p3 = e.clientX; p4 = e.clientY;
document.onmouseup = () => { document.onmouseup = null; document.onmousemove = null; saveConfig(); };
document.onmousemove = (e) => {
p1 = p3 - e.clientX; p2 = p4 - e.clientY;
p3 = e.clientX; p4 = e.clientY;
container.style.top = (container.offsetTop - p2) + "px";
container.style.left = (container.offsetLeft - p1) + "px";
config.x = container.offsetLeft; config.y = container.offsetTop;
};
};
}
// --- Quiz Detection ---
document.addEventListener('click', (e) => {
if (!config.autoCapture) return;
const text = document.body.innerText;
if (!(text.includes('Submit') || text.includes('Questions'))) return;
let choice = "";
const target = e.target;
if (target.type === 'radio') {
const parentText = target.parentElement.innerText;
const match = parentText.match(/([a-hA-H])[\)\.]/);
choice = match ? match[1] : target.value;
} else {
const match = target.innerText?.trim().match(/^([a-hA-H])[\)\.]/);
if (match) choice = match[1];
}
if (choice) {
const qNum = findQ(target);
if (qNum) saveAnswer(qNum, choice);
}
}, true);
function findQ(el) {
let curr = el;
for(let i=0; i<8; i++) {
if(!curr) break;
const match = curr.innerText?.match(/(\d+)\)\s/);
if(match) return match[1];
curr = curr.parentElement;
}
return document.body.innerText.match(/(\d+)\)\s/)?.[1];
}
function saveConfig() { localStorage.setItem(CONFIG_KEY, JSON.stringify(config)); }
updateStructure();
renderList();
})();