update
This commit is contained in:
@@ -1,37 +1,692 @@
|
||||
// /public/assets/js/fakecheck.js
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Werte kommen aus globalen Variablen, die wir in PHP setzen:
|
||||
const baseUrl = window.fakecheckBaseUrl || "";
|
||||
const locale = window.fakecheckLocale || "en";
|
||||
|
||||
const startButton = document.getElementById("startButton");
|
||||
const resultContainer = document.getElementById("resultContainer");
|
||||
const resultOutput = document.getElementById("resultOutput");
|
||||
const root = document.getElementById("fc-root");
|
||||
if (!root) {
|
||||
// Auf anderen Seiten eingebunden? Dann einfach nichts tun.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!startButton || !resultContainer || !resultOutput) return;
|
||||
// --- DOM-Helper ---------------------------------------------------------
|
||||
const $ = (sel) => document.querySelector(sel);
|
||||
const $$ = (sel) => Array.from(document.querySelectorAll(sel));
|
||||
|
||||
startButton.addEventListener("click", () => {
|
||||
const now = new Date().toISOString();
|
||||
const logEl = $("#fc-log");
|
||||
const statusTextEl = $("#fc-status-text");
|
||||
const statusModeEl = $("#fc-status-mode");
|
||||
const progressInner = $("#fc-progress-inner");
|
||||
const overallPill = $("#fc-overall-status-pill");
|
||||
const overallStatus = $("#fc-overall-status-text");
|
||||
const fsapiWarning = $("#fc-fsapi-warning");
|
||||
const selectedPathText = $("#fc-selected-path-label");
|
||||
const saveHint = $("#fc-save-hint");
|
||||
const saveError = $("#fc-save-error");
|
||||
|
||||
const demoReport = {
|
||||
meta: {
|
||||
tool: baseUrl,
|
||||
mode: "browser-demo",
|
||||
timestamp: now,
|
||||
locale: locale
|
||||
},
|
||||
tests: [
|
||||
{ id: "quick_test", label: "Quick-Test (Demo)", status: "pending" },
|
||||
{ id: "light_benchmark", label: "Light-Benchmark (Demo)", status: "pending" },
|
||||
{ id: "write_verify", label: "Write/Verify (Demo)", status: "pending" }
|
||||
],
|
||||
note: "Dies ist nur eine Platzhalter-Ausgabe. Die echte Web-Testlogik (File System Access, Fortschritt, realer JSON-Report) implementieren wir im nächsten Schritt."
|
||||
const resMode = $("#fc-res-mode");
|
||||
const resDuration = $("#fc-res-duration");
|
||||
const resWriteSpeed = $("#fc-res-write-speed");
|
||||
const resReadSpeed = $("#fc-res-read-speed");
|
||||
const resWritten = $("#fc-res-written-bytes");
|
||||
const resVerified = $("#fc-res-verified-bytes");
|
||||
|
||||
const btnPickDir = $("#fc-btn-pick-directory");
|
||||
const btnClearSel = $("#fc-btn-clear-selection");
|
||||
const btnStart = $("#fc-btn-start-tests");
|
||||
const btnCancel = $("#fc-btn-cancel-tests");
|
||||
const modeTiles = $$("#fc-mode-grid .fc-mode-tile");
|
||||
|
||||
// Falls du einen Login-Indikator hast, kannst du ihn global setzen,
|
||||
// z. B. window.fakecheckIsLoggedIn = true/false
|
||||
if (window.fakecheckIsLoggedIn) {
|
||||
saveHint.style.display = "block";
|
||||
}
|
||||
|
||||
function logLine(message, level = "info") {
|
||||
if (!logEl) return;
|
||||
const line = document.createElement("div");
|
||||
line.className = "fc-log-line";
|
||||
const prefix =
|
||||
level === "error" ? "[ERROR] " :
|
||||
level === "warn" ? "[WARN] " :
|
||||
"[INFO] ";
|
||||
line.innerHTML = `<strong>${prefix}</strong>${message}`;
|
||||
logEl.appendChild(line);
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
}
|
||||
|
||||
function setStatus(text) {
|
||||
if (statusTextEl) statusTextEl.textContent = text;
|
||||
}
|
||||
|
||||
function setModeLabel(text) {
|
||||
if (statusModeEl) statusModeEl.textContent = text;
|
||||
}
|
||||
|
||||
function setProgress(percent) {
|
||||
if (!progressInner) return;
|
||||
const v = Math.min(100, Math.max(0, percent));
|
||||
progressInner.style.width = v + "%";
|
||||
}
|
||||
|
||||
function setOverallStatus(state, text) {
|
||||
if (!overallPill || !overallStatus) return;
|
||||
overallPill.classList.remove("fc-pill-ok", "fc-pill-warn", "fc-pill-bad");
|
||||
if (state === "ok") overallPill.classList.add("fc-pill-ok");
|
||||
else if (state === "warn") overallPill.classList.add("fc-pill-warn");
|
||||
else overallPill.classList.add("fc-pill-bad");
|
||||
overallStatus.textContent = text;
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (bytes == null) return "–";
|
||||
const units = ["B", "KB", "MB", "GB", "TB"];
|
||||
let u = 0;
|
||||
let v = bytes;
|
||||
while (v >= 1024 && u < units.length - 1) {
|
||||
v /= 1024;
|
||||
u++;
|
||||
}
|
||||
return v.toFixed(1) + " " + units[u];
|
||||
}
|
||||
|
||||
function formatMbps(bytes, seconds) {
|
||||
if (!seconds || seconds <= 0) return "–";
|
||||
const bits = bytes * 8;
|
||||
const mbits = bits / (1000 * 1000);
|
||||
return mbits.toFixed(1) + " Mbit/s";
|
||||
}
|
||||
|
||||
function formatDuration(seconds) {
|
||||
if (!seconds) return "–";
|
||||
const s = Math.round(seconds);
|
||||
if (s < 60) return s + " s";
|
||||
const m = Math.floor(s / 60);
|
||||
const r = s % 60;
|
||||
if (m < 60) return `${m} min ${r}s`;
|
||||
const h = Math.floor(m / 60);
|
||||
const rm = m % 60;
|
||||
return `${h} h ${rm} min`;
|
||||
}
|
||||
|
||||
// --- Test-Engine (Browser) ---------------------------------------------
|
||||
|
||||
class UsbBrowserTester {
|
||||
constructor() {
|
||||
this.rootHandle = null;
|
||||
this.abortController = null;
|
||||
}
|
||||
|
||||
hasFsApiSupport() {
|
||||
return "showDirectoryPicker" in window;
|
||||
}
|
||||
|
||||
async pickDirectory() {
|
||||
if (!this.hasFsApiSupport()) {
|
||||
throw new Error("File System Access API wird von diesem Browser nicht unterstützt.");
|
||||
}
|
||||
const handle = await window.showDirectoryPicker();
|
||||
this.rootHandle = handle;
|
||||
return handle;
|
||||
}
|
||||
|
||||
async clearSelection() {
|
||||
this.rootHandle = null;
|
||||
}
|
||||
|
||||
async runQuickCheck(report, progressCb, abortSignal) {
|
||||
const TEST_FILENAME = "usbcheck_quick_test.bin";
|
||||
const TEST_SIZE_MB = 8;
|
||||
const CHUNK_SIZE = 1024 * 1024;
|
||||
|
||||
const dirHandle = this.rootHandle;
|
||||
if (!dirHandle) throw new Error("Kein Verzeichnis ausgewählt.");
|
||||
|
||||
logLine("Quick-Check: Vorbereitung...", "info");
|
||||
const fileHandle = await dirHandle.getFileHandle(TEST_FILENAME, { create: true });
|
||||
const writable = await fileHandle.createWritable();
|
||||
|
||||
const totalBytes = TEST_SIZE_MB * 1024 * 1024;
|
||||
let writtenBytes = 0;
|
||||
|
||||
const writeStart = performance.now();
|
||||
while (writtenBytes < totalBytes) {
|
||||
if (abortSignal.aborted) {
|
||||
await writable.abort();
|
||||
throw new DOMException("Abgebrochen", "AbortError");
|
||||
}
|
||||
const remaining = totalBytes - writtenBytes;
|
||||
const chunkLen = Math.min(CHUNK_SIZE, remaining);
|
||||
const chunk = new Uint8Array(chunkLen);
|
||||
for (let i = 0; i < chunkLen; i++) {
|
||||
chunk[i] = (i + writtenBytes) % 251;
|
||||
}
|
||||
await writable.write(chunk);
|
||||
writtenBytes += chunkLen;
|
||||
progressCb((writtenBytes / totalBytes) * 100 * 0.5);
|
||||
}
|
||||
await writable.close();
|
||||
const writeEnd = performance.now();
|
||||
|
||||
logLine("Quick-Check: Schreiben abgeschlossen. Verifiziere Daten...", "info");
|
||||
|
||||
const file = await fileHandle.getFile();
|
||||
const readStart = performance.now();
|
||||
|
||||
const reader = file.stream().getReader();
|
||||
let offset = 0;
|
||||
let verifiedBytes = 0;
|
||||
while (true) {
|
||||
if (abortSignal.aborted) {
|
||||
reader.cancel();
|
||||
throw new DOMException("Abgebrochen", "AbortError");
|
||||
}
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
const chunk = value;
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
const expected = (offset + i) % 251;
|
||||
if (chunk[i] !== expected) {
|
||||
logLine(`Quick-Check: Datenfehler bei Byte ${offset + i}`, "error");
|
||||
throw new Error(`Datenfehler im Quick-Check bei Byte ${offset + i}`);
|
||||
}
|
||||
}
|
||||
offset += chunk.length;
|
||||
verifiedBytes += chunk.length;
|
||||
progressCb(50 + (verifiedBytes / totalBytes) * 100 * 0.5);
|
||||
}
|
||||
|
||||
const readEnd = performance.now();
|
||||
|
||||
const writeSeconds = (writeEnd - writeStart) / 1000;
|
||||
const readSeconds = (readEnd - readStart) / 1000;
|
||||
|
||||
report.quick = {
|
||||
mode: "quick",
|
||||
test_file: TEST_FILENAME,
|
||||
size_bytes: totalBytes,
|
||||
write_bytes: writtenBytes,
|
||||
read_bytes: verifiedBytes,
|
||||
write_duration_s: writeSeconds,
|
||||
read_duration_s: readSeconds,
|
||||
write_mbit_s: writeSeconds ? (writtenBytes * 8 / (writeSeconds * 1e6)) : null,
|
||||
read_mbit_s: readSeconds ? (verifiedBytes * 8 / (readSeconds * 1e6)) : null,
|
||||
ok: true
|
||||
};
|
||||
|
||||
logLine("Quick-Check: Erfolgreich abgeschlossen.", "info");
|
||||
}
|
||||
|
||||
async runBenchmark(report, progressCb, abortSignal) {
|
||||
const TEST_FILENAME = "usbcheck_benchmark.bin";
|
||||
const TEST_SIZE_MB = 32;
|
||||
const CHUNK_SIZE = 1024 * 1024;
|
||||
|
||||
const dirHandle = this.rootHandle;
|
||||
if (!dirHandle) throw new Error("Kein Verzeichnis ausgewählt.");
|
||||
|
||||
logLine("Benchmark: Start – schreibe Testdatei...", "info");
|
||||
const fileHandle = await dirHandle.getFileHandle(TEST_FILENAME, { create: true });
|
||||
const writable = await fileHandle.createWritable();
|
||||
|
||||
const totalBytes = TEST_SIZE_MB * 1024 * 1024;
|
||||
let writtenBytes = 0;
|
||||
|
||||
const writeStart = performance.now();
|
||||
while (writtenBytes < totalBytes) {
|
||||
if (abortSignal.aborted) {
|
||||
await writable.abort();
|
||||
throw new DOMException("Abgebrochen", "AbortError");
|
||||
}
|
||||
const remaining = totalBytes - writtenBytes;
|
||||
const chunkLen = Math.min(CHUNK_SIZE, remaining);
|
||||
const chunk = new Uint8Array(chunkLen);
|
||||
for (let i = 0; i < chunkLen; i++) {
|
||||
chunk[i] = 0xaa;
|
||||
}
|
||||
await writable.write(chunk);
|
||||
writtenBytes += chunkLen;
|
||||
progressCb((writtenBytes / totalBytes) * 100 * 0.4);
|
||||
}
|
||||
await writable.close();
|
||||
const writeEnd = performance.now();
|
||||
|
||||
logLine("Benchmark: Lesen & Timing...", "info");
|
||||
|
||||
const file = await fileHandle.getFile();
|
||||
const readStart = performance.now();
|
||||
const reader = file.stream().getReader();
|
||||
let readBytes = 0;
|
||||
while (true) {
|
||||
if (abortSignal.aborted) {
|
||||
reader.cancel();
|
||||
throw new DOMException("Abgebrochen", "AbortError");
|
||||
}
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
readBytes += value.length;
|
||||
progressCb(40 + (readBytes / totalBytes) * 100 * 0.6);
|
||||
}
|
||||
const readEnd = performance.now();
|
||||
|
||||
const writeSeconds = (writeEnd - writeStart) / 1000;
|
||||
const readSeconds = (readEnd - readStart) / 1000;
|
||||
|
||||
report.benchmark = {
|
||||
mode: "benchmark",
|
||||
test_file: TEST_FILENAME,
|
||||
size_bytes: totalBytes,
|
||||
write_bytes: writtenBytes,
|
||||
read_bytes: readBytes,
|
||||
write_duration_s: writeSeconds,
|
||||
read_duration_s: readSeconds,
|
||||
write_mbit_s: writeSeconds ? (writtenBytes * 8 / (writeSeconds * 1e6)) : null,
|
||||
read_mbit_s: readSeconds ? (readBytes * 8 / (readSeconds * 1e6)) : null,
|
||||
ok: true
|
||||
};
|
||||
|
||||
logLine("Benchmark: Erfolgreich abgeschlossen.", "info");
|
||||
}
|
||||
|
||||
async runWriteVerify(report, progressCb, abortSignal) {
|
||||
const BASE_FILENAME = "usbcheck_block_";
|
||||
const BLOCKS = 4;
|
||||
const BLOCK_SIZE_MB = 32;
|
||||
const CHUNK_SIZE = 1024 * 1024;
|
||||
|
||||
const dirHandle = this.rootHandle;
|
||||
if (!dirHandle) throw new Error("Kein Verzeichnis ausgewählt.");
|
||||
|
||||
logLine("Write/Verify: Start – mehrere Blöcke werden getestet...", "info");
|
||||
|
||||
const totalBytes = BLOCKS * BLOCK_SIZE_MB * 1024 * 1024;
|
||||
let writtenBytes = 0;
|
||||
let verifiedBytes = 0;
|
||||
|
||||
const globalStart = performance.now();
|
||||
const writeDetails = [];
|
||||
const readDetails = [];
|
||||
|
||||
for (let b = 0; b < BLOCKS; b++) {
|
||||
const filename = `${BASE_FILENAME}${String(b + 1).padStart(2, "0")}.bin`;
|
||||
logLine(`Write/Verify: Block ${b + 1}/${BLOCKS} – ${filename}`, "info");
|
||||
|
||||
const fileHandle = await dirHandle.getFileHandle(filename, { create: true });
|
||||
const writable = await fileHandle.createWritable();
|
||||
|
||||
const blockBytes = BLOCK_SIZE_MB * 1024 * 1024;
|
||||
let blockWritten = 0;
|
||||
const blockWriteStart = performance.now();
|
||||
|
||||
while (blockWritten < blockBytes) {
|
||||
if (abortSignal.aborted) {
|
||||
await writable.abort();
|
||||
throw new DOMException("Abgebrochen", "AbortError");
|
||||
}
|
||||
const remaining = blockBytes - blockWritten;
|
||||
const chunkLen = Math.min(CHUNK_SIZE, remaining);
|
||||
const chunk = new Uint8Array(chunkLen);
|
||||
for (let i = 0; i < chunkLen; i++) {
|
||||
chunk[i] = (i + blockWritten + b * 13) % 251;
|
||||
}
|
||||
await writable.write(chunk);
|
||||
blockWritten += chunkLen;
|
||||
writtenBytes += chunkLen;
|
||||
const progress = (writtenBytes + verifiedBytes) / (totalBytes * 2) * 100;
|
||||
progressCb(progress);
|
||||
}
|
||||
await writable.close();
|
||||
const blockWriteEnd = performance.now();
|
||||
writeDetails.push({
|
||||
block: b + 1,
|
||||
bytes: blockBytes,
|
||||
duration_s: (blockWriteEnd - blockWriteStart) / 1000
|
||||
});
|
||||
|
||||
const file = await fileHandle.getFile();
|
||||
const reader = file.stream().getReader();
|
||||
let blockOffset = 0;
|
||||
const blockReadStart = performance.now();
|
||||
|
||||
while (true) {
|
||||
if (abortSignal.aborted) {
|
||||
reader.cancel();
|
||||
throw new DOMException("Abgebrochen", "AbortError");
|
||||
}
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
const chunk = value;
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
const expected = (i + blockOffset + b * 13) % 251;
|
||||
if (chunk[i] !== expected) {
|
||||
logLine(`Write/Verify: Datenfehler in Block ${b + 1} bei Byte ${blockOffset + i}`, "error");
|
||||
throw new Error(`Datenfehler in Block ${b + 1} bei Byte ${blockOffset + i}`);
|
||||
}
|
||||
}
|
||||
blockOffset += chunk.length;
|
||||
verifiedBytes += chunk.length;
|
||||
const progress = (writtenBytes + verifiedBytes) / (totalBytes * 2) * 100;
|
||||
progressCb(progress);
|
||||
}
|
||||
const blockReadEnd = performance.now();
|
||||
readDetails.push({
|
||||
block: b + 1,
|
||||
bytes: blockBytes,
|
||||
duration_s: (blockReadEnd - blockReadStart) / 1000
|
||||
});
|
||||
}
|
||||
|
||||
const globalEnd = performance.now();
|
||||
const totalDuration = (globalEnd - globalStart) / 1000;
|
||||
|
||||
const sumWrite = writeDetails.reduce((acc, d) => acc + d.bytes, 0);
|
||||
const sumRead = readDetails.reduce((acc, d) => acc + d.bytes, 0);
|
||||
const writeSec = writeDetails.reduce((acc, d) => acc + d.duration_s, 0);
|
||||
const readSec = readDetails.reduce((acc, d) => acc + d.duration_s, 0);
|
||||
|
||||
report.writeverify = {
|
||||
mode: "writeverify",
|
||||
blocks: BLOCKS,
|
||||
block_size_mb: BLOCK_SIZE_MB,
|
||||
total_bytes: totalBytes,
|
||||
written_bytes: writtenBytes,
|
||||
verified_bytes: verifiedBytes,
|
||||
write_duration_s: writeSec,
|
||||
read_duration_s: readSec,
|
||||
write_mbit_s: writeSec ? (sumWrite * 8 / (writeSec * 1e6)) : null,
|
||||
read_mbit_s: readSec ? (sumRead * 8 / (readSec * 1e6)) : null,
|
||||
ok: true
|
||||
};
|
||||
|
||||
report.writeverify_total_duration_s = totalDuration;
|
||||
|
||||
logLine("Write/Verify: Alle Blöcke erfolgreich verifiziert.", "info");
|
||||
}
|
||||
|
||||
async run(mode, updateProgressCb, abortSignal) {
|
||||
if (!this.rootHandle) {
|
||||
throw new Error("Kein USB-Verzeichnis ausgewählt.");
|
||||
}
|
||||
|
||||
const report = {
|
||||
meta: {
|
||||
base_url: baseUrl,
|
||||
locale: locale,
|
||||
user_agent: navigator.userAgent,
|
||||
started_at: new Date().toISOString()
|
||||
},
|
||||
tool: "usbcheck_browser",
|
||||
tool_version: "0.1.0",
|
||||
mode_requested: mode,
|
||||
quick: null,
|
||||
benchmark: null,
|
||||
writeverify: null,
|
||||
total_duration_s: null
|
||||
};
|
||||
|
||||
const t0 = performance.now();
|
||||
|
||||
if (mode === "quick") {
|
||||
await this.runQuickCheck(report, updateProgressCb, abortSignal);
|
||||
} else if (mode === "benchmark") {
|
||||
await this.runBenchmark(report, updateProgressCb, abortSignal);
|
||||
} else if (mode === "writeverify") {
|
||||
await this.runWriteVerify(report, updateProgressCb, abortSignal);
|
||||
} else if (mode === "all") {
|
||||
const modes = ["quick", "benchmark", "writeverify"];
|
||||
for (let i = 0; i < modes.length; i++) {
|
||||
const subMode = modes[i];
|
||||
logLine(`All-Inclusive: Starte Teiltest "${subMode}" (${i + 1}/${modes.length})...`, "info");
|
||||
await this.run(
|
||||
subMode,
|
||||
(p) => {
|
||||
const base = (i / modes.length) * 100;
|
||||
const span = (1 / modes.length) * 100;
|
||||
updateProgressCb(base + (p / 100) * span);
|
||||
},
|
||||
abortSignal
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Unbekannter Modus: " + mode);
|
||||
}
|
||||
|
||||
const t1 = performance.now();
|
||||
report.total_duration_s = (t1 - t0) / 1000;
|
||||
report.meta.ended_at = new Date().toISOString();
|
||||
|
||||
return report;
|
||||
}
|
||||
}
|
||||
|
||||
const tester = new UsbBrowserTester();
|
||||
let currentMode = null;
|
||||
let isRunning = false;
|
||||
|
||||
if (!tester.hasFsApiSupport()) {
|
||||
if (fsapiWarning) fsapiWarning.style.display = "block";
|
||||
logLine(
|
||||
"Dein Browser unterstützt die File System Access API nicht voll. Einige Funktionen sind deaktiviert.",
|
||||
"warn"
|
||||
);
|
||||
}
|
||||
|
||||
function updateStartButtonState() {
|
||||
const hasDir = !!tester.rootHandle;
|
||||
const hasMode = !!currentMode;
|
||||
if (btnStart) btnStart.disabled = !(hasDir && hasMode && !isRunning);
|
||||
if (btnCancel) btnCancel.disabled = !isRunning;
|
||||
}
|
||||
|
||||
// --- Event-Handler ------------------------------------------------------
|
||||
|
||||
if (btnPickDir) {
|
||||
btnPickDir.addEventListener("click", async () => {
|
||||
try {
|
||||
const handle = await tester.pickDirectory();
|
||||
if (selectedPathText) {
|
||||
selectedPathText.textContent =
|
||||
'USB-Ordner ausgewählt (Name: "' + (handle.name || "Unbekannt") + '").';
|
||||
}
|
||||
if (btnClearSel) btnClearSel.disabled = false;
|
||||
setStatus("USB-Verzeichnis ausgewählt. Wähle jetzt einen Testmodus.");
|
||||
logLine("Verzeichnis ausgewählt: " + (handle.name || "[ohne Namen]"));
|
||||
updateStartButtonState();
|
||||
} catch (err) {
|
||||
if (err.name === "AbortError") {
|
||||
logLine("Verzeichnisauswahl abgebrochen.", "warn");
|
||||
} else {
|
||||
logLine("Fehler bei Verzeichnisauswahl: " + err.message, "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnClearSel) {
|
||||
btnClearSel.addEventListener("click", async () => {
|
||||
await tester.clearSelection();
|
||||
if (selectedPathText) {
|
||||
selectedPathText.textContent = "Noch kein Verzeichnis gewählt.";
|
||||
}
|
||||
btnClearSel.disabled = true;
|
||||
setStatus("Bereit. Wähle zuerst deinen USB-Stick aus.");
|
||||
logLine("Verzeichnisauswahl zurückgesetzt.", "info");
|
||||
updateStartButtonState();
|
||||
});
|
||||
}
|
||||
|
||||
modeTiles.forEach((tile) => {
|
||||
tile.addEventListener("click", () => {
|
||||
if (tile.classList.contains("disabled")) return;
|
||||
if (isRunning) return;
|
||||
modeTiles.forEach((t) => t.classList.remove("selected"));
|
||||
tile.classList.add("selected");
|
||||
currentMode = tile.getAttribute("data-mode");
|
||||
const titleEl = tile.querySelector("h4");
|
||||
const label = titleEl ? titleEl.textContent : currentMode;
|
||||
setModeLabel(label || "");
|
||||
setStatus(`Modus "${label}" ausgewählt. Du kannst den Test jetzt starten.`);
|
||||
logLine("Modus gewählt: " + currentMode, "info");
|
||||
updateStartButtonState();
|
||||
});
|
||||
});
|
||||
|
||||
if (btnStart) {
|
||||
btnStart.addEventListener("click", async () => {
|
||||
if (!currentMode || !tester.rootHandle || isRunning) return;
|
||||
isRunning = true;
|
||||
updateStartButtonState();
|
||||
if (btnCancel) btnCancel.disabled = false;
|
||||
if (saveError) saveError.style.display = "none";
|
||||
setProgress(0);
|
||||
logLine("Starte Tests im Modus: " + currentMode.toUpperCase(), "info");
|
||||
setStatus("Test läuft... bitte USB-Stick nicht entfernen.");
|
||||
setOverallStatus("warn", "Test läuft...");
|
||||
|
||||
const abortController = new AbortController();
|
||||
tester.abortController = abortController;
|
||||
|
||||
const updateProgressCb = (percent) => setProgress(percent);
|
||||
let report = null;
|
||||
|
||||
try {
|
||||
report = await tester.run(currentMode, updateProgressCb, abortController.signal);
|
||||
|
||||
setProgress(100);
|
||||
setStatus("Test abgeschlossen.");
|
||||
setOverallStatus("ok", "Browser-Test erfolgreich abgeschlossen.");
|
||||
applyReportToDashboard(report);
|
||||
|
||||
// Debug / Support:
|
||||
console.log("USB Browser Test Report (fakecheck):", report);
|
||||
|
||||
// Ergebnis speichern: Backend entscheidet, ob der User eingeloggt ist
|
||||
await saveReportToBackend(report);
|
||||
} catch (err) {
|
||||
if (err.name === "AbortError") {
|
||||
setStatus("Test wurde abgebrochen.");
|
||||
setOverallStatus("warn", "Test abgebrochen.");
|
||||
logLine("Test wurde vom Benutzer abgebrochen.", "warn");
|
||||
} else {
|
||||
setStatus("Fehler: " + err.message);
|
||||
setOverallStatus("bad", "Fehler im Browser-Test.");
|
||||
logLine("Fehler im Test: " + err.message, "error");
|
||||
}
|
||||
} finally {
|
||||
isRunning = false;
|
||||
tester.abortController = null;
|
||||
updateStartButtonState();
|
||||
if (btnCancel) btnCancel.disabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnCancel) {
|
||||
btnCancel.addEventListener("click", () => {
|
||||
if (!isRunning || !tester.abortController) return;
|
||||
tester.abortController.abort();
|
||||
});
|
||||
}
|
||||
|
||||
// --- Dashboard-Füllung --------------------------------------------------
|
||||
|
||||
function applyReportToDashboard(report) {
|
||||
const modeLabelMap = {
|
||||
quick: "Quick-Check",
|
||||
benchmark: "Benchmark",
|
||||
writeverify: "Write & Verify",
|
||||
all: "All-Inclusive (alle Browser-Tests)"
|
||||
};
|
||||
|
||||
resultOutput.textContent = JSON.stringify(demoReport, null, 2);
|
||||
if (resMode) {
|
||||
resMode.textContent = modeLabelMap[report.mode_requested] || report.mode_requested || "–";
|
||||
}
|
||||
if (resDuration) {
|
||||
resDuration.textContent = formatDuration(report.total_duration_s);
|
||||
}
|
||||
|
||||
resultContainer.classList.remove("hidden");
|
||||
resultContainer.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
});
|
||||
let aggWriteBytes = 0;
|
||||
let aggReadBytes = 0;
|
||||
let writeSec = 0;
|
||||
let readSec = 0;
|
||||
|
||||
if (report.quick) {
|
||||
aggWriteBytes += report.quick.write_bytes || 0;
|
||||
aggReadBytes += report.quick.read_bytes || 0;
|
||||
writeSec += report.quick.write_duration_s || 0;
|
||||
readSec += report.quick.read_duration_s || 0;
|
||||
}
|
||||
if (report.benchmark) {
|
||||
aggWriteBytes += report.benchmark.write_bytes || 0;
|
||||
aggReadBytes += report.benchmark.read_bytes || 0;
|
||||
writeSec += report.benchmark.write_duration_s || 0;
|
||||
readSec += report.benchmark.read_duration_s || 0;
|
||||
}
|
||||
if (report.writeverify) {
|
||||
aggWriteBytes += report.writeverify.written_bytes || 0;
|
||||
aggReadBytes += report.writeverify.verified_bytes || 0;
|
||||
writeSec += report.writeverify.write_duration_s || 0;
|
||||
readSec += report.writeverify.read_duration_s || 0;
|
||||
}
|
||||
|
||||
if (resWritten) {
|
||||
resWritten.textContent = aggWriteBytes ? formatBytes(aggWriteBytes) : "–";
|
||||
}
|
||||
if (resVerified) {
|
||||
resVerified.textContent = aggReadBytes ? formatBytes(aggReadBytes) : "–";
|
||||
}
|
||||
|
||||
const avgWriteMbps = (aggWriteBytes && writeSec) ? formatMbps(aggWriteBytes, writeSec) : "–";
|
||||
const avgReadMbps = (aggReadBytes && readSec) ? formatMbps(aggReadBytes, readSec) : "–";
|
||||
|
||||
if (resWriteSpeed) resWriteSpeed.textContent = avgWriteMbps;
|
||||
if (resReadSpeed) resReadSpeed.textContent = avgReadMbps;
|
||||
}
|
||||
|
||||
// --- Backend-Speicherung ------------------------------------------------
|
||||
|
||||
async function saveReportToBackend(report) {
|
||||
// Absoluter Pfad – dein Script liegt oberhalb des public-Roots:
|
||||
const url = "/api/result/browser-quick-test.php";
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(report),
|
||||
credentials: "include"
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// 401 => nicht eingeloggt; 500 => Fehler etc.
|
||||
const msg = "HTTP " + response.status;
|
||||
logLine("Backend: Konnte Ergebnis nicht speichern (" + msg + ").", "warn");
|
||||
if (response.status >= 500 && saveError) {
|
||||
saveError.style.display = "block";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json().catch(() => null);
|
||||
logLine(
|
||||
"Backend: Testergebnis gespeichert" +
|
||||
(data && data.id ? ` (ID: ${data.id})` : ""),
|
||||
"info"
|
||||
);
|
||||
} catch (err) {
|
||||
if (saveError) saveError.style.display = "block";
|
||||
logLine("Fehler beim Speichern im Backend: " + err.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Initialzustand -----------------------------------------------------
|
||||
|
||||
setStatus("Bereit. Wähle zuerst deinen USB-Stick aus.");
|
||||
setModeLabel("Kein Modus selektiert");
|
||||
setOverallStatus("ok", "Noch kein Test durchgeführt.");
|
||||
logLine("USB-Browser-Test (fakecheck) geladen. Warte auf Verzeichnisauswahl und Modus.");
|
||||
updateStartButtonState();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
// public/assets/js/header.js
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const supportedLangs = ['de', 'en', 'it', 'fr'];
|
||||
|
||||
function resolveCurrentLang() {
|
||||
const url = new URL(window.location.href);
|
||||
const urlLang = (url.searchParams.get('lang') || '').toLowerCase();
|
||||
const docLang = (document.documentElement.getAttribute('lang') || '').toLowerCase();
|
||||
const globalLang = (window.currentLang || '').toLowerCase();
|
||||
|
||||
if (supportedLangs.includes(urlLang)) return urlLang;
|
||||
if (supportedLangs.includes(globalLang)) return globalLang;
|
||||
if (supportedLangs.includes(docLang)) return docLang;
|
||||
return 'de';
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// LOGIN-BUTTON → /login mit redirect + lang
|
||||
// --------------------------------------------------
|
||||
@@ -13,12 +27,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const currentQuery = window.location.search || '';
|
||||
const redirect = currentPath + currentQuery;
|
||||
|
||||
// Sprache aus dem Label oben ziehen, falls vorhanden
|
||||
const langLabelEl = document.getElementById('langCurrentLabel');
|
||||
let lang = 'de';
|
||||
if (langLabelEl && langLabelEl.textContent) {
|
||||
lang = langLabelEl.textContent.trim().toLowerCase();
|
||||
}
|
||||
// Sprache aus URL / globalem State ableiten
|
||||
const lang = resolveCurrentLang();
|
||||
|
||||
// Wenn wir bereits auf /login sind → nur zum #auth scrollen
|
||||
if (currentPath === '/login' || currentPath === '/login/') {
|
||||
|
||||
Reference in New Issue
Block a user