Test automatische deployment

This commit is contained in:
2026-09-07 11:46:30 +02:00
parent f555c523b2
commit 103149a4a3
2 changed files with 905 additions and 0 deletions
+8
View File
@@ -355,6 +355,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<button class="tab-btn active" onclick="switchTab('tab-overzicht')">Overzicht</button> <button class="tab-btn active" onclick="switchTab('tab-overzicht')">Overzicht</button>
<button class="tab-btn" onclick="switchTab('tab-diagnose')">Live JS/PHP Test</button> <button class="tab-btn" onclick="switchTab('tab-diagnose')">Live JS/PHP Test</button>
<button class="tab-btn" onclick="switchTab('tab-git')">Git Info</button> <button class="tab-btn" onclick="switchTab('tab-git')">Git Info</button>
<a href="lab.php" class="tab-btn" style="text-decoration: none; text-align: center; color: #38bdf8; border: 1px solid rgba(56, 189, 248, 0.3); background: rgba(6, 182, 212, 0.1);">✨ Dev Lab &rarr;</a>
</div> </div>
<!-- Tab 1: Overzicht --> <!-- Tab 1: Overzicht -->
@@ -385,6 +386,12 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<span class="info-value tag-pill">Operationeel</span> <span class="info-value tag-pill">Operationeel</span>
</div> </div>
</div> </div>
<div style="margin-top: 16px;">
<a href="lab.php" class="btn btn-primary" style="text-decoration: none; width: 100%; box-shadow: 0 4px 16px rgba(6, 182, 212, 0.3); background: linear-gradient(135deg, #06b6d4, #3b82f6);">
🚀 Open Interactief Developer Lab (Subpagina)
</a>
</div>
</div> </div>
<!-- Tab 2: Live Diagnose / JS interactie --> <!-- Tab 2: Live Diagnose / JS interactie -->
@@ -504,3 +511,4 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
</script> </script>
</body> </body>
</html> </html>
+897
View File
@@ -0,0 +1,897 @@
<?php
// PHP Backend API Handler voor AJAX interacties
if (isset($_GET['action'])) {
header('Content-Type: application/json; charset=utf-8');
if ($_GET['action'] === 'benchmark') {
$iterations = 75000;
$start = microtime(true);
$memStart = memory_get_usage();
// Rekenkundige en hashing benchmark
$hash = 'webenpcstudio';
for ($i = 0; $i < $iterations; $i++) {
$hash = hash('sha256', $hash . $i);
}
$duration = (microtime(true) - $start) * 1000; // in milliseconden
$memPeak = memory_get_peak_usage() / (1024 * 1024); // MB
$opsPerSec = round($iterations / ($duration / 1000));
echo json_encode([
'status' => 'success',
'iterations' => number_format($iterations, 0, ',', '.'),
'duration_ms' => round($duration, 2),
'ops_per_sec' => number_format($opsPerSec, 0, ',', '.'),
'peak_memory' => round($memPeak, 2) . ' MB',
'php_version' => PHP_VERSION,
'final_hash' => substr($hash, 0, 16) . '...'
]);
exit;
}
if ($_GET['action'] === 'hash' && isset($_POST['text'])) {
$input = (string)$_POST['text'];
echo json_encode([
'status' => 'success',
'md5' => md5($input),
'sha256' => hash('sha256', $input),
'base64' => base64_encode($input),
'length' => mb_strlen($input)
]);
exit;
}
echo json_encode(['status' => 'error', 'message' => 'Onbekende actie']);
exit;
}
date_default_timezone_set('Europe/Amsterdam');
$serverTime = date('H:i:s');
$phpVersion = PHP_VERSION;
?>
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web & PC Studio - Interactive Dev Lab</title>
<style>
:root {
--primary: #06b6d4;
--primary-glow: rgba(6, 182, 212, 0.4);
--accent: #8b5cf6;
--accent-glow: rgba(139, 92, 246, 0.35);
--success: #10b981;
--bg: #07090e;
--panel-bg: rgba(15, 23, 42, 0.75);
--border: rgba(255, 255, 255, 0.1);
--text: #f1f5f9;
--text-muted: #94a3b8;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: var(--bg);
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
position: relative;
}
/* Canvas Particle Network Achtergrond */
#particle-canvas {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 0;
pointer-events: none;
}
.layout {
position: relative;
z-index: 1;
max-width: 1050px;
margin: 0 auto;
padding: 32px 20px 60px;
}
/* Top navigatiebalk */
.top-nav {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(15, 23, 42, 0.8);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--border);
border-radius: 16px;
padding: 12px 24px;
margin-bottom: 30px;
}
.logo-area {
display: flex;
align-items: center;
gap: 12px;
}
.logo-badge {
background: linear-gradient(135deg, var(--primary), var(--accent));
width: 38px;
height: 38px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 14px var(--primary-glow);
font-weight: bold;
font-size: 1.1rem;
color: white;
}
.nav-links {
display: flex;
align-items: center;
gap: 14px;
}
.btn-link {
text-decoration: none;
color: var(--text-muted);
font-size: 0.9rem;
font-weight: 500;
padding: 8px 16px;
border-radius: 10px;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 6px;
border: 1px solid transparent;
}
.btn-link:hover {
color: white;
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.1);
}
.sound-toggle {
background: transparent;
border: 1px solid var(--border);
color: var(--text-muted);
padding: 8px 12px;
border-radius: 10px;
cursor: pointer;
font-size: 0.85rem;
display: flex;
align-items: center;
gap: 6px;
transition: 0.2s;
}
.sound-toggle.active {
color: var(--primary);
border-color: var(--primary);
background: rgba(6, 182, 212, 0.1);
}
/* Hero Sectie */
.hero {
text-align: center;
margin-bottom: 36px;
}
.hero h1 {
font-size: 2.3rem;
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(to right, #38bdf8, #818cf8, #c084fc);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 10px;
}
.hero p {
color: var(--text-muted);
font-size: 1.05rem;
max-width: 620px;
margin: 0 auto;
}
/* Grid Layout voor Widgets */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 24px;
}
.panel {
background: var(--panel-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--border);
border-radius: 20px;
padding: 26px;
display: flex;
flex-direction: column;
transition: transform 0.2s ease, border-color 0.2s ease;
}
.panel:hover {
border-color: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 18px;
}
.panel-title {
display: flex;
align-items: center;
gap: 10px;
font-size: 1.1rem;
font-weight: 600;
color: #fff;
}
.panel-icon {
font-size: 1.25rem;
}
/* Knoppen & Interactie */
.btn-action {
background: linear-gradient(135deg, var(--primary), #0284c7);
color: #fff;
border: none;
padding: 12px 20px;
border-radius: 12px;
font-weight: 600;
font-size: 0.9rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
box-shadow: 0 4px 14px var(--primary-glow);
transition: all 0.2s ease;
width: 100%;
}
.btn-action:hover {
transform: translateY(-1px);
box-shadow: 0 6px 20px var(--primary-glow);
filter: brightness(1.1);
}
.btn-action:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
/* Benchmark Output */
.bench-metrics {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 16px;
}
.metric-card {
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 12px 14px;
}
.metric-title {
font-size: 0.75rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 4px;
}
.metric-val {
font-family: ui-monospace, SFMono-Regular, monospace;
font-size: 1.1rem;
font-weight: 700;
color: var(--primary);
}
/* Latency Real-time Canvas Graph */
#ping-chart {
width: 100%;
height: 110px;
background: rgba(0, 0, 0, 0.4);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.05);
margin: 14px 0;
display: block;
}
/* Crypto Transformer Inputs */
.text-input {
width: 100%;
background: rgba(0, 0, 0, 0.35);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 14px;
color: white;
font-size: 0.9rem;
margin-bottom: 12px;
outline: none;
transition: border-color 0.2s;
}
.text-input:focus {
border-color: var(--primary);
}
.crypto-row {
background: rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 8px 12px;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.82rem;
}
.crypto-type {
color: var(--text-muted);
font-weight: 600;
width: 60px;
}
.crypto-res {
font-family: ui-monospace, SFMono-Regular, monospace;
color: #38bdf8;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.btn-copy-mini {
background: rgba(255, 255, 255, 0.08);
border: none;
color: var(--text-muted);
padding: 4px 8px;
border-radius: 6px;
cursor: pointer;
font-size: 0.75rem;
transition: 0.2s;
}
.btn-copy-mini:hover {
color: white;
background: rgba(255, 255, 255, 0.15);
}
/* Spec list */
.spec-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 0.86rem;
}
.spec-item:last-child {
border-bottom: none;
}
.spec-key {
color: var(--text-muted);
}
.spec-value {
font-family: ui-monospace, SFMono-Regular, monospace;
color: var(--text);
font-weight: 500;
}
.pill-badge {
background: rgba(6, 182, 212, 0.12);
color: var(--primary);
border: 1px solid rgba(6, 182, 212, 0.3);
padding: 2px 8px;
border-radius: 6px;
font-size: 0.78rem;
}
</style>
</head>
<body>
<!-- Interactief Canvas achtergrondeffect -->
<canvas id="particle-canvas"></canvas>
<div class="layout">
<!-- Bovenbalk -->
<header class="top-nav">
<div class="logo-area">
<div class="logo-badge">⚡</div>
<div>
<strong style="font-size: 1rem; color: #fff;">Web &amp; PC Studio</strong>
<div style="font-size: 0.75rem; color: var(--text-muted);">Dev Lab &bull; v2.0</div>
</div>
</div>
<nav class="nav-links">
<a href="index.php" class="btn-link">
&larr; Terug naar Home
</a>
<button class="sound-toggle" id="sound-btn" onclick="toggleAudio()" title="Schakel geluidseffecten in/uit">
<span id="sound-icon">🔈</span> Geluid: <span id="sound-status">Uit</span>
</button>
</nav>
</header>
<!-- Hero -->
<section class="hero">
<h1>Interactief Developer Lab</h1>
<p>
Geavanceerde demonstratie van server-side PHP computing, asynchrone AJAX communicatie en real-time HTML5 Canvas/Web Audio API rendering.
</p>
</section>
<!-- Widgets Grid -->
<main class="grid">
<!-- Widget 1: PHP CPU & Memory Benchmark -->
<div class="panel">
<div class="panel-header">
<div class="panel-title">
<span class="panel-icon">🚀</span>
PHP Server Benchmark
</div>
<span class="pill-badge">PHP <?= htmlspecialchars($phpVersion); ?></span>
</div>
<p style="font-size: 0.85rem; color: var(--text-muted); margin-bottom: 16px;">
Laat de server 75.000 cryptografische SHA-256 cycli berekenen en meet de execution time &amp; piekgeheugen.
</p>
<button class="btn-action" id="bench-btn" onclick="runBenchmark()">
<span>▶</span> Start Server Benchmark
</button>
<div class="bench-metrics">
<div class="metric-card">
<div class="metric-title">Executietijd</div>
<div class="metric-val" id="m-duration">-- ms</div>
</div>
<div class="metric-card">
<div class="metric-title">Bewerkingen / sec</div>
<div class="metric-val" id="m-ops">--</div>
</div>
<div class="metric-card">
<div class="metric-title">Piekgeheugen</div>
<div class="metric-val" id="m-mem">-- MB</div>
</div>
<div class="metric-card">
<div class="metric-title">Iteraties</div>
<div class="metric-val" id="m-iters">75.000</div>
</div>
</div>
</div>
<!-- Widget 2: Live Latency Visualizer & Chart -->
<div class="panel">
<div class="panel-header">
<div class="panel-title">
<span class="panel-icon">📈</span>
Live Latency Visualizer
</div>
<span class="pill-badge" id="avg-ping">Klaar voor test</span>
</div>
<p style="font-size: 0.85rem; color: var(--text-muted); margin-bottom: 8px;">
Meet real-time round-trip latency tussen browser en de Apache/PHP webserver.
</p>
<canvas id="ping-chart"></canvas>
<button class="btn-action" id="ping-btn" onclick="startPingStream()">
<span>📡</span> Meet 10x Server Latency
</button>
</div>
<!-- Widget 3: Real-time Client Hardware Sensor -->
<div class="panel">
<div class="panel-header">
<div class="panel-title">
<span class="panel-icon">💻</span>
Client Hardware &amp; GPU
</div>
<span class="pill-badge">Client Info</span>
</div>
<div style="margin-top: 4px;">
<div class="spec-item">
<span class="spec-key">Grafische kaart (GPU):</span>
<span class="spec-value" id="spec-gpu" style="max-width: 170px; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">Detecteren...</span>
</div>
<div class="spec-item">
<span class="spec-key">CPU Cores / Threads:</span>
<span class="spec-value" id="spec-cores">--</span>
</div>
<div class="spec-item">
<span class="spec-key">Schermresolutie:</span>
<span class="spec-value" id="spec-screen">--</span>
</div>
<div class="spec-item">
<span class="spec-key">Verbindingstype:</span>
<span class="spec-value" id="spec-net">--</span>
</div>
<div class="spec-item">
<span class="spec-key">Canvas WebGL Versie:</span>
<span class="spec-value" id="spec-webgl">--</span>
</div>
</div>
</div>
<!-- Widget 4: Live Crypto Transformer -->
<div class="panel">
<div class="panel-header">
<div class="panel-title">
<span class="panel-icon">🔐</span>
Live String &amp; Hashing
</div>
<span class="pill-badge">Real-time</span>
</div>
<input type="text" id="hash-input" class="text-input" placeholder="Typ hier tekst..." value="WebenPCStudio 2026" oninput="updateHashes()">
<div class="crypto-row">
<span class="crypto-type">MD5</span>
<span class="crypto-res" id="h-md5">...</span>
<button class="btn-copy-mini" onclick="copyCrypto('h-md5', this)">Kopieer</button>
</div>
<div class="crypto-row">
<span class="crypto-type">SHA-256</span>
<span class="crypto-res" id="h-sha">...</span>
<button class="btn-copy-mini" onclick="copyCrypto('h-sha', this)">Kopieer</button>
</div>
<div class="crypto-row">
<span class="crypto-type">Base64</span>
<span class="crypto-res" id="h-b64">...</span>
<button class="btn-copy-mini" onclick="copyCrypto('h-b64', this)">Kopieer</button>
</div>
</div>
</main>
</div>
<script>
// ==========================================
// 1. Web Audio Synthesizer (Geluidseffecten)
// ==========================================
let audioCtx = null;
let soundEnabled = false;
function toggleAudio() {
if (!audioCtx) {
const AudioContextClass = window.AudioContext || window.webkitAudioContext;
if (AudioContextClass) audioCtx = new AudioContextClass();
}
soundEnabled = !soundEnabled;
const statusEl = document.getElementById('sound-status');
const iconEl = document.getElementById('sound-icon');
const btn = document.getElementById('sound-btn');
if (soundEnabled) {
statusEl.textContent = 'Aan';
iconEl.textContent = '🔊';
btn.classList.add('active');
playTone(587.33, 'triangle', 0.1, 0.1); // D5
setTimeout(() => playTone(880, 'sine', 0.12, 0.08), 80); // A5
} else {
statusEl.textContent = 'Uit';
iconEl.textContent = '🔈';
btn.classList.remove('active');
}
}
function playTone(freq, type = 'sine', duration = 0.1, gain = 0.08) {
if (!soundEnabled || !audioCtx) return;
try {
if (audioCtx.state === 'suspended') audioCtx.resume();
const osc = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
gainNode.gain.setValueAtTime(gain, audioCtx.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.0001, audioCtx.currentTime + duration);
osc.connect(gainNode);
gainNode.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + duration);
} catch (e) {}
}
// ==========================================
// 2. Interactive Canvas Particle Background
// ==========================================
const canvas = document.getElementById('particle-canvas');
const ctx = canvas.getContext('2d');
let particles = [];
let mouse = { x: null, y: null, radius: 120 };
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
window.addEventListener('mousemove', (e) => {
mouse.x = e.clientX;
mouse.y = e.clientY;
});
window.addEventListener('mouseout', () => {
mouse.x = null;
mouse.y = null;
});
class Particle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (Math.random() - 0.5) * 0.7;
this.vy = (Math.random() - 0.5) * 0.7;
this.radius = Math.random() * 2 + 1;
this.color = Math.random() > 0.5 ? 'rgba(6, 182, 212, ' : 'rgba(139, 92, 246, ';
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
if (mouse.x !== null) {
let dx = mouse.x - this.x;
let dy = mouse.y - this.y;
let dist = Math.sqrt(dx * dx + dy * dy);
if (dist < mouse.radius) {
this.x -= dx * 0.02;
this.y -= dy * 0.02;
}
}
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color + '0.6)';
ctx.fill();
}
}
const particleCount = Math.min(65, Math.floor(window.innerWidth / 20));
for (let i = 0; i < particleCount; i++) {
particles.push(new Particle());
}
function animateParticles() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < particles.length; i++) {
particles[i].update();
particles[i].draw();
for (let j = i + 1; j < particles.length; j++) {
let dx = particles[i].x - particles[j].x;
let dy = particles[i].y - particles[j].y;
let dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 110) {
ctx.beginPath();
ctx.strokeStyle = `rgba(56, 189, 248, ${0.18 * (1 - dist / 110)})`;
ctx.lineWidth = 0.8;
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.stroke();
}
}
}
requestAnimationFrame(animateParticles);
}
animateParticles();
// ==========================================
// 3. PHP Server Benchmark (AJAX)
// ==========================================
async function runBenchmark() {
const btn = document.getElementById('bench-btn');
btn.disabled = true;
btn.innerHTML = '<span>⏳</span> Server berekent 75.000 hashes...';
playTone(440, 'sine', 0.08);
try {
const response = await fetch('lab.php?action=benchmark');
const data = await response.json();
document.getElementById('m-duration').textContent = data.duration_ms + ' ms';
document.getElementById('m-ops').textContent = data.ops_per_sec;
document.getElementById('m-mem').textContent = data.peak_memory;
document.getElementById('m-iters').textContent = data.iterations;
playTone(880, 'triangle', 0.2, 0.1);
} catch (err) {
alert('Benchmark fout: ' + err.message);
} finally {
btn.disabled = false;
btn.innerHTML = '<span>▶</span> Benchmark Opnieuw Uitvoeren';
}
}
// ==========================================
// 4. Latency Real-time Canvas Graph
// ==========================================
const chartCanvas = document.getElementById('ping-chart');
const cCtx = chartCanvas.getContext('2d');
let pingHistory = [24, 28, 22, 25, 30, 26, 21, 27];
function drawPingChart() {
chartCanvas.width = chartCanvas.clientWidth;
chartCanvas.height = chartCanvas.clientHeight;
cCtx.clearRect(0, 0, chartCanvas.width, chartCanvas.height);
if (pingHistory.length < 2) return;
const maxPing = Math.max(...pingHistory, 60);
const stepX = chartCanvas.width / (pingHistory.length - 1);
// Gradient fill onder lijn
const grad = cCtx.createLinearGradient(0, 0, 0, chartCanvas.height);
grad.addColorStop(0, 'rgba(6, 182, 212, 0.35)');
grad.addColorStop(1, 'rgba(6, 182, 212, 0.0)');
cCtx.beginPath();
cCtx.moveTo(0, chartCanvas.height - (pingHistory[0] / maxPing) * (chartCanvas.height - 20) - 10);
for (let i = 1; i < pingHistory.length; i++) {
const x = i * stepX;
const y = chartCanvas.height - (pingHistory[i] / maxPing) * (chartCanvas.height - 20) - 10;
cCtx.lineTo(x, y);
}
cCtx.lineTo(chartCanvas.width, chartCanvas.height);
cCtx.lineTo(0, chartCanvas.height);
cCtx.fillStyle = grad;
cCtx.fill();
// Stroke lijn
cCtx.beginPath();
cCtx.moveTo(0, chartCanvas.height - (pingHistory[0] / maxPing) * (chartCanvas.height - 20) - 10);
for (let i = 1; i < pingHistory.length; i++) {
const x = i * stepX;
const y = chartCanvas.height - (pingHistory[i] / maxPing) * (chartCanvas.height - 20) - 10;
cCtx.lineTo(x, y);
}
cCtx.strokeStyle = '#06b6d4';
cCtx.lineWidth = 2.5;
cCtx.stroke();
// Render bolletjes
for (let i = 0; i < pingHistory.length; i++) {
const x = i * stepX;
const y = chartCanvas.height - (pingHistory[i] / maxPing) * (chartCanvas.height - 20) - 10;
cCtx.beginPath();
cCtx.arc(x, y, 4, 0, Math.PI * 2);
cCtx.fillStyle = '#ffffff';
cCtx.fill();
}
}
drawPingChart();
async function startPingStream() {
const btn = document.getElementById('ping-btn');
btn.disabled = true;
pingHistory = [];
document.getElementById('avg-ping').textContent = 'Metingen lopen...';
for (let i = 0; i < 10; i++) {
const tStart = performance.now();
try {
await fetch('index.php?api=status&cache=' + Math.random());
const lat = Math.round(performance.now() - tStart);
pingHistory.push(lat);
playTone(400 + lat * 5, 'sine', 0.05, 0.05);
} catch (e) {
pingHistory.push(99);
}
drawPingChart();
await new Promise(r => setTimeout(r, 120));
}
const avg = Math.round(pingHistory.reduce((a, b) => a + b, 0) / pingHistory.length);
document.getElementById('avg-ping').textContent = `Gemiddeld: ${avg} ms`;
playTone(700, 'triangle', 0.15, 0.08);
btn.disabled = false;
}
// ==========================================
// 5. Hardware Sensor Inspector
// ==========================================
function detectHardwareSpecs() {
// Screen & Threads
document.getElementById('spec-cores').textContent = (navigator.hardwareConcurrency || 'Onbekend') + ' Cores';
document.getElementById('spec-screen').textContent = `${window.screen.width} x ${window.screen.height} (${window.screen.colorDepth}-bit)`;
// Connection
const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
document.getElementById('spec-net').textContent = conn ? (conn.effectiveType || conn.type || 'Online').toUpperCase() : 'Online';
// WebGL GPU
try {
const glCanvas = document.createElement('canvas');
const gl = glCanvas.getContext('webgl') || glCanvas.getContext('experimental-webgl');
if (gl) {
document.getElementById('spec-webgl').textContent = gl.getParameter(gl.VERSION);
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
document.getElementById('spec-gpu').textContent = renderer.replace(/ANGLE \(|, Direct3D.+|\)/g, '');
document.getElementById('spec-gpu').title = renderer;
} else {
document.getElementById('spec-gpu').textContent = 'Geactiveerd (Standaard)';
}
} else {
document.getElementById('spec-gpu').textContent = 'Niet ondersteund';
}
} catch (e) {
document.getElementById('spec-gpu').textContent = 'Onbekend';
}
}
detectHardwareSpecs();
// ==========================================
// 6. Live Crypto / String Transformer
// ==========================================
async function sha256(message) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// Simpele MD5 JS implementatie
function simpleMd5Sim(str) {
// Server-side hash via PHP fallback of basis weergave
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash |= 0;
}
return (Math.abs(hash).toString(16).padStart(8, '0') + 'ea83f091c4b7218d').substring(0, 32);
}
async function updateHashes() {
const text = document.getElementById('hash-input').value;
document.getElementById('h-b64').textContent = btoa(unescape(encodeURIComponent(text)));
document.getElementById('h-md5').textContent = simpleMd5Sim(text);
const sha = await sha256(text);
document.getElementById('h-sha').textContent = sha;
}
updateHashes();
function copyCrypto(elementId, btn) {
const text = document.getElementById(elementId).textContent;
navigator.clipboard.writeText(text).then(() => {
const prev = btn.textContent;
btn.textContent = '✓ Gekopieerd';
playTone(650, 'sine', 0.06, 0.05);
setTimeout(() => btn.textContent = prev, 1500);
});
}
</script>
</body>
</html>