/** * Голосовой ввод для полей поиска. * Автоматически добавляет кнопку микрофона к каждому input с классом "voice-input". * Использует Web Speech API (Chrome, Edge, Safari). * Режим: зажать кнопку — говорить — отпустить. */ (function() { var activeRecognition = null; var activeBtn = null; function stopRecognition() { if (activeRecognition) { activeRecognition.stop(); activeRecognition = null; } if (activeBtn) { activeBtn.classList.remove('voice-input-btn--active'); activeBtn = null; } } function initVoiceInput(input) { var parent = input.parentNode; var wrap; var parentStyle = window.getComputedStyle(parent); if (parentStyle.position === 'relative') { wrap = parent; } else { wrap = document.createElement('div'); wrap.className = 'voice-input-wrap'; parent.insertBefore(wrap, input); wrap.appendChild(input); } var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'voice-input-btn'; btn.title = 'Зажмите и говорите'; btn.innerHTML = ''; wrap.appendChild(btn); var clearBtn = wrap.querySelector('.search-clear-btn'); if (clearBtn) { btn.classList.add('voice-input-btn--with-clear'); clearBtn.classList.add('search-clear-btn--with-voice'); input.style.paddingRight = '5em'; } function startRecording(e) { e.preventDefault(); e.stopPropagation(); var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { alert('Голосовой ввод не поддерживается в этом браузере. Используйте Chrome или Edge.'); return; } if (activeRecognition) { stopRecognition(); return; } var recognition = new SpeechRecognition(); recognition.lang = 'ru-RU'; recognition.interimResults = true; recognition.continuous = true; recognition.maxAlternatives = 1; activeRecognition = recognition; activeBtn = btn; btn.classList.add('voice-input-btn--active'); // Очищаем поле и ставим фокус при начале записи input.value = ''; input.focus(); recognition.onresult = function(event) { var finalText = ''; var interimText = ''; for (var i = 0; i < event.results.length; i++) { if (event.results[i].isFinal) { finalText += event.results[i][0].transcript; } else { interimText += event.results[i][0].transcript; } } input.value = finalText + interimText; }; recognition.onerror = function() { stopRecognition(); }; recognition.onend = function() { if (activeBtn === btn) { btn.classList.remove('voice-input-btn--active'); activeRecognition = null; activeBtn = null; } // Снимаем фокус и запускаем поиск input.blur(); if (input.value.length >= 3) { // Симулируем нажатие Enter для запуска поиска var enterEvent = new KeyboardEvent('keydown', { key: 'Enter', keyCode: 13, which: 13, bubbles: true }); input.dispatchEvent(enterEvent); } }; recognition.start(); } function endRecording(e) { e.preventDefault(); e.stopPropagation(); if (activeRecognition && activeBtn === btn) { stopRecognition(); } } btn.addEventListener('mousedown', startRecording); btn.addEventListener('mouseup', endRecording); btn.addEventListener('mouseleave', endRecording); btn.addEventListener('touchstart', startRecording, { passive: false }); btn.addEventListener('touchend', endRecording, { passive: false }); btn.addEventListener('touchcancel', endRecording, { passive: false }); btn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); }); } function init() { var inputs = document.querySelectorAll('input.voice-input'); for (var i = 0; i < inputs.length; i++) { initVoiceInput(inputs[i]); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();