|
|
@@ -23,6 +23,22 @@ const quiz = [
|
|
|
|
|
|
function tone(type = 'click') { if (!soundEnabled || !window.AudioContext) return; const ctx = new AudioContext(); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); const now = ctx.currentTime; osc.type = type === 'error' ? 'sawtooth' : 'sine'; osc.frequency.setValueAtTime(type === 'success' ? 780 : type === 'error' ? 140 : 350, now); if (type === 'success') osc.frequency.exponentialRampToValueAtTime(1180, now + .12); gain.gain.setValueAtTime(.06, now); gain.gain.exponentialRampToValueAtTime(.001, now + (type === 'error' ? .3 : .12)); osc.start(now); osc.stop(now + .35); }
|
|
|
function setLog(text) { $('#eventLog').textContent = text; }
|
|
|
+const attemptHistory = [];
|
|
|
+const attemptLogList = $('#attemptLogList');
|
|
|
+function recordAttempt(code, result) {
|
|
|
+ attemptHistory.unshift({ code, result, time: new Date() });
|
|
|
+ attemptHistory.length = Math.min(attemptHistory.length, 5);
|
|
|
+ renderAttemptHistory();
|
|
|
+}
|
|
|
+function renderAttemptHistory() {
|
|
|
+ $('#clearAttemptLog').disabled = attemptHistory.length === 0;
|
|
|
+ if (!attemptHistory.length) {
|
|
|
+ attemptLogList.innerHTML = '<li class="attempt-log__empty">Попыток пока нет.</li>';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ attemptLogList.innerHTML = attemptHistory.map(({code, result, time}) => `<li class="attempt-log__item attempt-log__item--${result === 'Успех' ? 'success' : 'error'}"><span class="attempt-log__code">${code.split('').join(' ')}</span><span class="attempt-log__result">${result}</span><time datetime="${time.toISOString()}">${time.toLocaleTimeString('ru-RU', {hour:'2-digit', minute:'2-digit', second:'2-digit'})}</time></li>`).join('');
|
|
|
+}
|
|
|
+$('#clearAttemptLog').onclick = () => { attemptHistory.length = 0; renderAttemptHistory(); };
|
|
|
function updateCircuit(state = circuitState) {
|
|
|
circuitState = state;
|
|
|
const powered = state === 'open';
|
|
|
@@ -92,9 +108,11 @@ function openLock(fromExit = false) {
|
|
|
$('#lamp').className = 'lamp is-success';
|
|
|
$('#powerDot').classList.add('is-on');
|
|
|
setLog(fromExit ? 'S2 удерживается. Прямое питание YA1; нажмите S2 повторно, чтобы отпустить.' : 'Код принят. VT5 / VT2 включили YA1. Замок открыт до нажатия «Закрыть замок» или «Сбросить».');
|
|
|
+ if (!fromExit) recordAttempt(entered.join(''), 'Успех');
|
|
|
tone('success');
|
|
|
}
|
|
|
-function incorrect() {
|
|
|
+function incorrect(key) {
|
|
|
+ recordAttempt(entered.join('') + key, 'Ошибка');
|
|
|
entered = [];
|
|
|
circuitState = 'error';
|
|
|
updateScreen();
|
|
|
@@ -108,7 +126,7 @@ function input(key) {
|
|
|
tone();
|
|
|
pulseInput(key);
|
|
|
const candidate = entered.join('') + key;
|
|
|
- if (!activeCode.startsWith(candidate)) return incorrect();
|
|
|
+ if (!activeCode.startsWith(candidate)) return incorrect(key);
|
|
|
entered.push(key);
|
|
|
updateScreen();
|
|
|
if (candidate === activeCode) return openLock();
|