« Back to History
typing_master_special_keys.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /* ============================================================================= File: /typing_master_special_keys.php Purpose: NumPad special-keys practice (Enter, ., +) with proper layout & finger guide Author: mistermanager.in Date: 2025-10-08 ============================================================================= */ ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>NumPad Special Keys Practice — Enter / . / +</title> <style> body{ font-family: Segoe UI, Arial, sans-serif; background: #f4f6f8; color: #222; padding: 18px; text-align: center; } h1{margin:4px 0 10px;font-size:22px} .controls{margin:12px 0} button, select{ padding:8px 12px; border-radius:8px; border:0; cursor:pointer; margin:0 6px; font-size:14px; } button.start{background:#0d6efd;color:#fff} button.mode{background:#198754;color:#fff} #practice-area{ background:#fff; padding:14px; border-radius:10px; display:inline-block; box-shadow:0 2px 6px rgba(0,0,0,0.08); min-width:480px; } #practice-text{ font-size:26px; letter-spacing:3px; margin:6px 0; line-height:1.6; word-break:break-all; } .token{ display:inline-block; padding:6px 8px; border-radius:6px; margin:4px; font-weight:600; background:#eef2ff; color:#0b57d0; } .token.enter{background:#ffeedd;color:#b65a00} .token.dot{background:#e9f7ee;color:#0a7a3a} .token.plus{background:#fff0f6;color:#9c1850} /* ---- Correct keyboard grid layout ---- */ #visual-keyboard{ display:grid; grid-template-columns: repeat(4, 76px); grid-auto-rows: 76px; grid-auto-flow: row; gap:8px; justify-content:center; align-items:center; margin-top:16px; } #visual-keyboard .key{ width:76px; height:76px; display:flex; align-items:center; justify-content:center; background:#e9ecef; border-radius:8px; font-size:20px; box-shadow:0 1px 3px rgba(0,0,0,0.06); user-select:none; } .key.highlight{background:#0d6efd;color:#fff;transform:translateY(-2px)} @media(max-width:480px){ #visual-keyboard{grid-template-columns:repeat(4,56px);grid-auto-rows:56px;gap:6px;} #visual-keyboard .key{width:56px;height:56px;font-size:18px;} } #finger-guide{ display:flex; gap:12px; justify-content:center; margin-top:14px; } .finger{ width:56px; height:56px; border-radius:50%; opacity:0.35; display:flex; align-items:center; justify-content:center; font-weight:700; } .finger.little{background:#f44336;color:#fff} .finger.active{opacity:1;box-shadow:0 4px 10px rgba(0,0,0,0.12)} #stats{margin-top:12px;font-size:15px} #input-capture{ width:420px; padding:10px; font-size:16px; border-radius:8px; border:1px solid #cfd8dc; text-align:center; margin-top:10px; } .note{font-size:13px;color:#666;margin-top:8px} </style> </head> <body> <h1>NumPad — Enter / . / + Practice</h1> <div class="controls"> <select id="mode-select"> <option value="full">Full (numbers + . + + + Enter)</option> <option value="special">Only . + ↵ (recommended)</option> <option value="drill">Drill: single-key repeat</option> </select> <select id="drill-key" style="display:none"> <option value=".">Dot (.)</option> <option value="+">Plus (+)</option> <option value="Enter">Enter (↵)</option> </select> <button class="mode" id="gen-btn">Generate</button> <button class="start" id="start-btn">Start Practice</button> <button id="reset-btn">Reset</button> </div> <div id="practice-area"> <div id="practice-text">Click Generate → Start Practice</div> <input id="input-capture" placeholder="Type here. Press Enter to record ↵" autocomplete="off" /> <div class="note">Note: For Enter, press the Enter key — it will be recorded as ↵. Do not paste text.</div> <!-- Correctly ordered keypad --> <div id="visual-keyboard"> <div class="key" data-key="7">7</div> <div class="key" data-key="8">8</div> <div class="key" data-key="9">9</div> <div class="key" data-key="/">/</div> <div class="key" data-key="4">4</div> <div class="key" data-key="5">5</div> <div class="key" data-key="6">6</div> <div class="key" data-key="*">*</div> <div class="key" data-key="1">1</div> <div class="key" data-key="2">2</div> <div class="key" data-key="3">3</div> <div class="key" data-key="-">-</div> <div class="key" data-key="0">0</div> <div class="key" data-key=".">.</div> <div class="key" data-key="Enter">↵</div> <div class="key" data-key="+">+</div> </div> <div id="finger-guide"> <div class="finger little" id="little-f">Little</div> </div> <div id="stats"> Accuracy: <span id="acc">0</span>% | Speed: <span id="spd">0</span> CPM | Typed: <span id="typed">0</span> </div> </div> <script> const practiceTextEl = document.getElementById('practice-text'); const input = document.getElementById('input-capture'); const keys = [...document.querySelectorAll('.key')]; const startBtn = document.getElementById('start-btn'); const genBtn = document.getElementById('gen-btn'); const resetBtn = document.getElementById('reset-btn'); const modeSelect = document.getElementById('mode-select'); const drillKeySelect = document.getElementById('drill-key'); const accEl = document.getElementById('acc'); const spdEl = document.getElementById('spd'); const typedEl = document.getElementById('typed'); let tokens = []; let typedTokens = []; let startTime = null; let totalTyped = 0, correctTyped = 0; let running = false; function tokenToDisplay(t){ return t === 'Enter' ? '↵' : t; } function randomDigit(){ return String(Math.floor(Math.random()*10)); } function randomChoice(arr){ return arr[Math.floor(Math.random()*arr.length)]; } function generateSequence(mode='full', drillKey='Enter'){ const seq = []; if(mode === 'drill'){ for(let i=0;i<25;i++){ seq.push(drillKey); if(i%5===4) seq.push(randomDigit()); } } else if(mode === 'special'){ for(let i=0;i<28;i++){ const r = Math.random(); if(r < 0.35) seq.push(randomChoice(['.','+','Enter'])); else seq.push(randomDigit()); if(i%6===5) seq.push(randomChoice(['.','+'])); } } else { for(let i=0;i<30;i++){ seq.push(randomDigit()); if(i%4===3) seq.push(randomChoice(['.','+','Enter'])); } } return seq; } function renderTokens(){ practiceTextEl.innerHTML = tokens.map((t, idx) => { const cls = (t === 'Enter') ? 'token enter' : (t === '.') ? 'token dot' : (t === '+') ? 'token plus' : 'token'; return `<span class="${cls}" data-idx="${idx}">${tokenToDisplay(t)}</span>`; }).join(' '); } function resetStats(){ typedTokens = []; startTime = null; totalTyped=0; correctTyped=0; running=false; accEl.textContent='0'; spdEl.textContent='0'; typedEl.textContent='0'; input.value=''; highlightKey(null); document.getElementById('little-f').classList.remove('active'); } function highlightKey(key){ keys.forEach(k=>k.classList.toggle('highlight', k.dataset.key === key)); if(['Enter','.','+'].includes(key)) document.getElementById('little-f').classList.add('active'); else document.getElementById('little-f').classList.remove('active'); } input.addEventListener('keydown', e=>{ if(!running){ e.preventDefault(); return; } if(!startTime) startTime = Date.now(); if(e.key === 'Enter'){ e.preventDefault(); recordToken('Enter'); } }); input.addEventListener('input', e=>{ if(!running){ input.value=''; return; } const v = e.target.value; const lastChar = v.slice(-1); if(/[0-9\.\+]/.test(lastChar)){ recordToken(lastChar); input.value=''; } else input.value=''; }); function recordToken(tok){ totalTyped++; typedTokens.push(tok); const pos = typedTokens.length - 1; if(tokens[pos] === tok) correctTyped++; updateStats(); updateVisualProgress(); if(typedTokens.length >= tokens.length){ running=false; highlightKey(null); } else highlightKey(tokens[typedTokens.length]); } function updateStats(){ const acc = totalTyped?((correctTyped/totalTyped)*100).toFixed(1):0; accEl.textContent = acc; const elapsedMin = Math.max((Date.now() - (startTime||Date.now()))/60000,0.0001); spdEl.textContent = Math.round(correctTyped / elapsedMin); typedEl.textContent = totalTyped; } function updateVisualProgress(){ const spans = practiceTextEl.querySelectorAll('.token'); spans.forEach((s,i)=>{ s.style.opacity=0.45; s.style.textDecoration=''; s.style.border=''; if(i<typedTokens.length){ if(typedTokens[i]===tokens[i]){s.style.opacity=1;s.style.border='2px solid #cfe9d8';} else{s.style.opacity=1;s.style.border='2px solid #f8d7da';s.style.textDecoration='line-through';} } }); } genBtn.onclick=()=>{ tokens = generateSequence(modeSelect.value, drillKeySelect.value); renderTokens(); resetStats(); }; startBtn.onclick=()=>{ if(tokens.length===0){ alert('Please Generate sequence first.'); return; } resetStats(); running=true; input.focus(); highlightKey(tokens[0]); }; resetBtn.onclick=()=>{ tokens=[]; renderTokens(); resetStats(); practiceTextEl.textContent='Click Generate → Start Practice'; }; modeSelect.onchange=()=>{drillKeySelect.style.display=(modeSelect.value==='drill')?'inline-block':'none';}; tokens = generateSequence('special'); renderTokens(); input.addEventListener('paste', e=>e.preventDefault()); keys.forEach(k=>k.onclick=()=>{ if(!running)return; recordToken(k.dataset.key); }); </script> </body> </html>