// Глобальные переменные let socket = null; let androidSocket = null; let operatorSocket = null; let localStream = null; let peerConnection = null; let currentSessionId = null; // Конфигурация WebRTC const rtcConfig = { iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' } ] }; // Инициализация при загрузке страницы document.addEventListener('DOMContentLoaded', function() { initializeSocket(); loadSystemStats(); // Обновление статистики каждые 5 секунд setInterval(loadSystemStats, 5000); }); // Управление вкладками function showTab(tabName) { // Скрываем все вкладки document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); // Убираем активный класс с кнопок document.querySelectorAll('.tab').forEach(btn => { btn.classList.remove('active'); }); // Показываем выбранную вкладку document.getElementById(tabName).classList.add('active'); event.target.classList.add('active'); } // Инициализация основного сокета для мониторинга function initializeSocket() { socket = io(); socket.on('connect', () => { logMessage('info', 'Подключение к серверу установлено'); updateConnectionStatus(true); }); socket.on('disconnect', () => { logMessage('warn', 'Подключение к серверу потеряно'); updateConnectionStatus(false); }); socket.on('device:connected', (data) => { logMessage('info', `Устройство подключено: ${data.deviceId}`); }); socket.on('device:disconnected', (data) => { logMessage('warn', `Устройство отключено: ${data.deviceId}`); }); // Обработчики событий сессий socket.on('session:created', (data) => { logMessage('info', `Сессия создана: ${data.sessionId} для устройства ${data.deviceId}`); updateOperatorSessions(); }); socket.on('session:accepted', (data) => { logMessage('info', `Сессия принята: ${data.sessionId}`); currentSessionId = data.sessionId; document.getElementById('current-session-id').value = currentSessionId; // Показываем кнопку веб-камеры для оператора showOperatorWebcamButton(); updateOperatorSessions(); showAlert('Сессия принята! Можно начинать WebRTC соединение.', 'success'); }); socket.on('session:rejected', (data) => { logMessage('warn', `Сессия отклонена: ${data.sessionId} - ${data.error}`); updateOperatorSessions(); showAlert(`Сессия отклонена: ${data.error}`, 'warning'); }); socket.on('session:ended', (data) => { logMessage('info', `Сессия завершена: ${data.sessionId}`); updateOperatorSessions(); // Скрываем элементы веб-камеры hideOperatorWebcamButton(); const webcamVideo = document.getElementById('operatorWebcam'); if (webcamVideo) webcamVideo.style.display = 'none'; }); } // Обновление статуса подключения function updateConnectionStatus(connected) { const statusCard = document.getElementById('connection-status'); const statusText = document.getElementById('connection-text'); if (connected) { statusCard.className = 'status-card status-connected'; statusText.textContent = '✅ Подключено к серверу'; } else { statusCard.className = 'status-card status-disconnected'; statusText.textContent = '❌ Нет подключения к серверу'; } } // Загрузка системной статистики async function loadSystemStats() { try { const response = await fetch('/api/status'); const data = await response.json(); if (data.devices && data.sessions) { document.getElementById('stat-devices').textContent = data.devices.connectedDevices || 0; document.getElementById('stat-operators').textContent = data.devices.connectedOperators || 0; document.getElementById('stat-sessions').textContent = data.sessions.activeSessions || 0; document.getElementById('stat-uptime').textContent = Math.round(data.uptime / 60) || 0; } } catch (error) { console.error('Ошибка загрузки статистики:', error); } } // Система логирования function logMessage(level, message) { const logs = document.getElementById('system-logs'); const timestamp = new Date().toLocaleTimeString(); const logEntry = document.createElement('div'); logEntry.className = `log-entry log-${level}`; logEntry.textContent = `[${timestamp}] ${message}`; logs.appendChild(logEntry); logs.scrollTop = logs.scrollHeight; // Ограничиваем количество логов const maxLogs = 100; while (logs.children.length > maxLogs) { logs.removeChild(logs.firstChild); } } function clearLogs() { document.getElementById('system-logs').innerHTML = ''; } // === ANDROID DEVICE SIMULATION === function connectAndroid() { const deviceId = document.getElementById('android-device-id').value; if (!deviceId) { alert('Введите Device ID'); return; } androidSocket = io(); androidSocket.on('connect', () => { const deviceInfo = { model: document.getElementById('android-model').value, manufacturer: document.getElementById('android-manufacturer').value, androidVersion: document.getElementById('android-version').value, availableCameras: Array.from(document.getElementById('android-cameras').selectedOptions) .map(option => option.value).join(',') }; androidSocket.emit('register:android', { deviceId, deviceInfo }); logMessage('info', `Android устройство подключается: ${deviceId}`); }); androidSocket.on('register:success', (data) => { logMessage('info', `Android устройство зарегистрировано: ${data.deviceId}`); document.getElementById('android-connect').disabled = true; document.getElementById('android-disconnect').disabled = false; }); androidSocket.on('camera:request', (data) => { showCameraRequest(data); }); androidSocket.on('camera:switch', (data) => { logMessage('info', `Запрос переключения камеры: ${data.cameraType}`); showAlert('info', `Переключение камеры на: ${data.cameraType}`); }); androidSocket.on('camera:disconnect', (data) => { logMessage('warn', `Сессия завершена: ${data.sessionId}`); removeCameraSession(data.sessionId); }); setupAndroidWebRTC(); } function disconnectAndroid() { if (androidSocket) { androidSocket.disconnect(); androidSocket = null; logMessage('warn', 'Android устройство отключено'); document.getElementById('android-connect').disabled = false; document.getElementById('android-disconnect').disabled = true; // Очищаем запросы и сессии document.getElementById('android-requests').innerHTML = ''; document.getElementById('android-sessions').innerHTML = ''; } } function showCameraRequest(data) { const container = document.getElementById('android-requests'); const requestDiv = document.createElement('div'); requestDiv.className = 'session-card session-pending'; requestDiv.innerHTML = `

Запрос камеры

Сессия: ${data.sessionId}

Оператор: ${data.operatorId}

Камера: ${data.cameraType}

`; container.appendChild(requestDiv); logMessage('info', `Получен запрос камеры от ${data.operatorId}`); } function acceptCameraRequest(sessionId) { if (androidSocket) { androidSocket.emit('camera:response', { sessionId, accepted: true, streamUrl: 'webrtc' }); // Перемещаем в активные сессии moveToActiveSessions(sessionId, 'active'); logMessage('info', `Запрос камеры принят: ${sessionId}`); } } function declineCameraRequest(sessionId) { if (androidSocket) { androidSocket.emit('camera:response', { sessionId, accepted: false, error: 'Пользователь отклонил запрос' }); // Удаляем запрос removeCameraRequest(sessionId); logMessage('warn', `Запрос камеры отклонен: ${sessionId}`); } } function moveToActiveSessions(sessionId, status) { removeCameraRequest(sessionId); const container = document.getElementById('android-sessions'); const sessionDiv = document.createElement('div'); sessionDiv.className = `session-card session-${status}`; sessionDiv.id = `android-session-${sessionId}`; sessionDiv.innerHTML = `

Активная сессия

ID: ${sessionId}

Статус: ${status}

`; container.appendChild(sessionDiv); currentSessionId = sessionId; document.getElementById('current-session-id').value = sessionId; } function removeCameraRequest(sessionId) { const container = document.getElementById('android-requests'); const requests = container.children; for (let i = 0; i < requests.length; i++) { if (requests[i].innerHTML.includes(sessionId)) { container.removeChild(requests[i]); break; } } } function removeCameraSession(sessionId) { const sessionElement = document.getElementById(`android-session-${sessionId}`); if (sessionElement) { sessionElement.remove(); } if (currentSessionId === sessionId) { currentSessionId = null; document.getElementById('current-session-id').value = ''; } } function endAndroidSession(sessionId) { if (androidSocket) { androidSocket.emit('camera:disconnect', { sessionId }); removeCameraSession(sessionId); } } // === OPERATOR SIMULATION === function connectOperator() { const operatorId = document.getElementById('operator-id').value; if (!operatorId) { alert('Введите Operator ID'); return; } operatorSocket = io(); operatorSocket.on('connect', () => { operatorSocket.emit('register:operator', { operatorId, operatorInfo: { name: 'Demo Operator', permissions: ['view_cameras', 'request_camera'] } }); logMessage('info', `Оператор подключается: ${operatorId}`); }); operatorSocket.on('register:success', (data) => { logMessage('info', `Оператор зарегистрирован: ${data.operatorId}`); document.getElementById('operator-connect').disabled = true; document.getElementById('operator-disconnect').disabled = false; showAvailableDevices(data.availableDevices || []); updateOperatorSessions(); // Обновляем список сессий после подключения // Автоматическое обновление сессий каждые 3 секунды setInterval(() => { if (operatorSocket && operatorSocket.connected) { updateOperatorSessions(); } }, 3000); }); operatorSocket.on('device:connected', (data) => { logMessage('info', `Новое устройство доступно: ${data.deviceId}`); refreshDevices(); }); operatorSocket.on('camera:stream-ready', (data) => { logMessage('info', `Камера готова: ${data.sessionId}`); showOperatorSession(data.sessionId, 'active'); updateOperatorSessions(); }); operatorSocket.on('camera:request-sent', (data) => { logMessage('info', `Запрос отправлен: ${data.sessionId}`); updateOperatorSessions(); }); operatorSocket.on('camera:denied', (data) => { logMessage('warn', `Запрос отклонен: ${data.error}`); showAlert('warning', `Запрос камеры отклонен: ${data.error}`); }); setupOperatorWebRTC(); } function disconnectOperator() { if (operatorSocket) { operatorSocket.disconnect(); operatorSocket = null; logMessage('warn', 'Оператор отключен'); document.getElementById('operator-connect').disabled = false; document.getElementById('operator-disconnect').disabled = true; // Очищаем списки document.getElementById('available-devices').innerHTML = ''; document.getElementById('operator-sessions').innerHTML = ''; } } async function refreshDevices() { const operatorId = document.getElementById('operator-id').value; if (!operatorId) return; try { const response = await fetch('/api/operators/devices', { headers: { 'X-Operator-Id': operatorId } }); if (response.ok) { const data = await response.json(); showAvailableDevices(data.devices || []); } } catch (error) { logMessage('error', 'Ошибка загрузки устройств: ' + error.message); } } function showAvailableDevices(devices) { const container = document.getElementById('available-devices'); container.innerHTML = ''; devices.forEach(device => { const deviceDiv = document.createElement('div'); deviceDiv.className = `device-card ${device.isConnected ? 'device-online' : 'device-offline'}`; deviceDiv.innerHTML = `

${device.deviceInfo.model || 'Unknown Device'}

ID: ${device.deviceId}

Статус: ${device.status}

Камеры: ${device.capabilities?.cameras?.join(', ') || 'Unknown'}

${device.canAcceptSession ? ` ` : '

Недоступен для новых сессий

' } `; container.appendChild(deviceDiv); }); } function getCameraName(cameraType) { const names = { 'back': 'Основная', 'front': 'Фронтальная', 'ultra_wide': 'Широкоугольная', 'telephoto': 'Телеобъектив' }; return names[cameraType] || cameraType; } async function requestCamera(deviceId) { const operatorId = document.getElementById('operator-id').value; const cameraSelect = document.getElementById(`camera-${deviceId}`); const cameraType = cameraSelect ? cameraSelect.value : 'back'; try { const response = await fetch('/api/operators/camera/request', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Operator-Id': operatorId }, body: JSON.stringify({ deviceId, cameraType }) }); if (response.ok) { const data = await response.json(); logMessage('info', `Запрос камеры отправлен: ${data.sessionId}`); showOperatorSession(data.sessionId, 'pending'); } else { const error = await response.json(); showAlert('danger', 'Ошибка запроса камеры: ' + error.error); } } catch (error) { logMessage('error', 'Ошибка запроса камеры: ' + error.message); } } function showOperatorSession(sessionId, status) { const container = document.getElementById('operator-sessions'); const existingSession = document.getElementById(`operator-session-${sessionId}`); if (existingSession) { // Обновляем существующую сессию existingSession.className = `session-card session-${status}`; const statusElement = existingSession.querySelector('.session-status'); if (statusElement) statusElement.textContent = status; return; } const sessionDiv = document.createElement('div'); sessionDiv.className = `session-card session-${status}`; sessionDiv.id = `operator-session-${sessionId}`; sessionDiv.innerHTML = `

Сессия камеры

ID: ${sessionId}

Статус: ${status}

`; container.appendChild(sessionDiv); currentSessionId = sessionId; document.getElementById('current-session-id').value = sessionId; } async function switchCamera(sessionId, cameraType) { const operatorId = document.getElementById('operator-id').value; try { const response = await fetch(`/api/operators/camera/${sessionId}/switch`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Operator-Id': operatorId }, body: JSON.stringify({ cameraType }) }); if (response.ok) { logMessage('info', `Переключение камеры: ${cameraType}`); showAlert('info', `Запрос переключения камеры на: ${getCameraName(cameraType)}`); } else { const error = await response.json(); showAlert('danger', 'Ошибка переключения камеры: ' + error.error); } } catch (error) { logMessage('error', 'Ошибка переключения камеры: ' + error.message); } } async function endOperatorSession(sessionId) { const operatorId = document.getElementById('operator-id').value; try { const response = await fetch(`/api/operators/camera/${sessionId}`, { method: 'DELETE', headers: { 'X-Operator-Id': operatorId } }); if (response.ok) { const sessionElement = document.getElementById(`operator-session-${sessionId}`); if (sessionElement) sessionElement.remove(); if (currentSessionId === sessionId) { currentSessionId = null; document.getElementById('current-session-id').value = ''; } logMessage('info', `Сессия завершена: ${sessionId}`); } } catch (error) { logMessage('error', 'Ошибка завершения сессии: ' + error.message); } } async function refreshSessions() { updateOperatorSessions(); } // === ADMIN FUNCTIONS === async function getSystemHealth() { try { const response = await fetch('/api/admin/health'); const data = await response.json(); const container = document.getElementById('admin-info'); container.innerHTML = `

Состояние системы: ${data.health.status}

Память: ${data.health.memory.used}MB / ${data.health.memory.total}MB

Время работы: ${data.health.uptime} сек

Подключения: ${data.health.connections.devices} устройств, ${data.health.connections.operators} операторов

${data.health.warnings ? `

Предупреждения: ${data.health.warnings.join(', ')}

` : ''}
`; } catch (error) { showAlert('danger', 'Ошибка проверки здоровья: ' + error.message); } } async function getSystemStats() { try { const response = await fetch('/api/admin/stats'); const data = await response.json(); const container = document.getElementById('admin-info'); container.innerHTML = `

Детальная статистика

${JSON.stringify(data.stats, null, 2)}
`; } catch (error) { showAlert('danger', 'Ошибка загрузки статистики: ' + error.message); } } async function cleanupSystem() { try { const response = await fetch('/api/admin/cleanup', { method: 'POST' }); const data = await response.json(); showAlert('success', `Очистка завершена. Удалено сессий: ${data.removedSessions}`); logMessage('info', `Системная очистка: удалено ${data.removedSessions} сессий`); } catch (error) { showAlert('danger', 'Ошибка очистки: ' + error.message); } } async function getAllDevices() { try { const response = await fetch('/api/admin/devices'); const data = await response.json(); const container = document.getElementById('all-devices'); container.innerHTML = ''; data.devices.forEach(device => { const deviceDiv = document.createElement('div'); deviceDiv.className = `device-card ${device.isConnected ? 'device-online' : 'device-offline'}`; deviceDiv.innerHTML = `
${device.deviceInfo.model || 'Unknown Device'}

ID: ${device.deviceId}

Статус: ${device.status}

Активных сессий: ${device.activeSessions}

Время работы: ${device.uptime} сек

`; container.appendChild(deviceDiv); }); } catch (error) { showAlert('danger', 'Ошибка загрузки устройств: ' + error.message); } } async function getAllSessions() { try { const response = await fetch('/api/admin/sessions'); const data = await response.json(); const container = document.getElementById('all-sessions'); container.innerHTML = ''; data.sessions.forEach(session => { const sessionDiv = document.createElement('div'); sessionDiv.className = `session-card session-${session.status}`; sessionDiv.innerHTML = `
Сессия ${session.sessionId}

Устройство: ${session.deviceId}

Оператор: ${session.operatorId}

Статус: ${session.status}

Продолжительность: ${session.duration}

Камера: ${getCameraName(session.cameraType)}

`; container.appendChild(sessionDiv); }); } catch (error) { showAlert('danger', 'Ошибка загрузки сессий: ' + error.message); } } // === WebRTC TEST === function setupAndroidWebRTC() { if (!androidSocket) return; androidSocket.on('webrtc:offer', async (data) => { try { const pc = getOrCreatePeerConnection(); await pc.setRemoteDescription(new RTCSessionDescription({ type: 'offer', sdp: data.offer })); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); androidSocket.emit('webrtc:answer', { sessionId: data.sessionId, answer: answer.sdp }); logMessage('info', 'WebRTC answer отправлен'); } catch (error) { logMessage('error', 'WebRTC ошибка: ' + error.message); } }); androidSocket.on('webrtc:ice-candidate', async (data) => { try { const pc = getOrCreatePeerConnection(); const candidate = JSON.parse(data.candidate); await pc.addIceCandidate(new RTCIceCandidate(candidate)); } catch (error) { logMessage('error', 'ICE candidate ошибка: ' + error.message); } }); } function setupOperatorWebRTC() { if (!operatorSocket) return; operatorSocket.on('webrtc:answer', async (data) => { try { const pc = getOrCreatePeerConnection(); await pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: data.answer })); logMessage('info', 'WebRTC answer получен'); } catch (error) { logMessage('error', 'WebRTC ошибка: ' + error.message); } }); operatorSocket.on('webrtc:ice-candidate', async (data) => { try { const pc = getOrCreatePeerConnection(); const candidate = JSON.parse(data.candidate); await pc.addIceCandidate(new RTCIceCandidate(candidate)); } catch (error) { logMessage('error', 'ICE candidate ошибка: ' + error.message); } }); } function getOrCreatePeerConnection() { if (!peerConnection) { peerConnection = new RTCPeerConnection(rtcConfig); peerConnection.onicecandidate = (event) => { if (event.candidate) { const candidateData = JSON.stringify({ candidate: event.candidate.candidate, sdpMid: event.candidate.sdpMid, sdpMLineIndex: event.candidate.sdpMLineIndex }); if (androidSocket) { androidSocket.emit('webrtc:ice-candidate', { sessionId: currentSessionId, candidate: candidateData }); } else if (operatorSocket) { operatorSocket.emit('webrtc:ice-candidate', { sessionId: currentSessionId, candidate: candidateData }); } } }; peerConnection.ontrack = (event) => { const remoteVideo = document.getElementById('remoteVideo'); remoteVideo.srcObject = event.streams[0]; remoteVideo.style.display = 'block'; logMessage('info', 'Получен удаленный видеопоток'); }; peerConnection.onconnectionstatechange = () => { const state = peerConnection.connectionState; logMessage('info', `WebRTC состояние: ${state}`); const statusDiv = document.getElementById('webrtc-status'); statusDiv.innerHTML = `
WebRTC состояние: ${state}
`; }; } return peerConnection; } async function startLocalVideo() { try { localStream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 }, audio: true }); const localVideo = document.getElementById('localVideo'); localVideo.srcObject = localStream; const pc = getOrCreatePeerConnection(); localStream.getTracks().forEach(track => { pc.addTrack(track, localStream); }); logMessage('info', 'Локальное видео запущено'); } catch (error) { logMessage('error', 'Ошибка доступа к камере: ' + error.message); showAlert('danger', 'Ошибка доступа к камере: ' + error.message); } } function stopLocalVideo() { if (localStream) { localStream.getTracks().forEach(track => track.stop()); localStream = null; const localVideo = document.getElementById('localVideo'); localVideo.srcObject = null; logMessage('info', 'Локальное видео остановлено'); } if (peerConnection) { peerConnection.close(); peerConnection = null; const remoteVideo = document.getElementById('remoteVideo'); remoteVideo.srcObject = null; remoteVideo.style.display = 'none'; logMessage('info', 'WebRTC соединение закрыто'); } } // Обновление списка сессий оператора function updateOperatorSessions() { console.log('updateOperatorSessions called, operatorSocket:', operatorSocket); if (!operatorSocket) { console.log('No operatorSocket, returning'); return; } if (!operatorSocket.connected) { console.log('operatorSocket not connected, returning'); return; } console.log('Requesting sessions list...'); operatorSocket.emit('sessions:list', {}, (sessions) => { console.log('Received sessions:', sessions); const container = document.getElementById('operator-sessions'); if (!container) { console.log('No operator-sessions container found'); return; } container.innerHTML = ''; if (!sessions || sessions.length === 0) { container.innerHTML = '

Нет активных сессий

'; console.log('No sessions to display'); return; } console.log('Displaying', sessions.length, 'sessions'); sessions.forEach(session => { const sessionCard = document.createElement('div'); sessionCard.className = `session-card session-${session.status}`; const statusText = { 'pending': 'Ожидание', 'active': 'Активна', 'rejected': 'Отклонена', 'ended': 'Завершена' }; sessionCard.innerHTML = `

Сессия: ${session.sessionId ? session.sessionId.substr(0, 8) : session.id ? session.id.substr(0, 8) : 'N/A'}...

Устройство: ${session.deviceId}

Камера: ${session.cameraType}

Статус: ${statusText[session.status] || session.status}

Создана: ${new Date(session.createdAt).toLocaleString()}

${session.status === 'active' ? ` ` : ''} `; container.appendChild(sessionCard); }); }); } // Завершение сессии function endSession(sessionId) { if (!operatorSocket) { showAlert('Не подключен как оператор', 'danger'); return; } operatorSocket.emit('session:end', { sessionId }); logMessage('info', `Завершаем сессию: ${sessionId}`); } // Переключение камеры function switchCamera(sessionId, cameraType) { if (!operatorSocket) { showAlert('Не подключен как оператор', 'danger'); return; } operatorSocket.emit('camera:switch', { sessionId, cameraType }); logMessage('info', `Переключаем камеру: ${sessionId} на ${cameraType}`); } // Показ уведомления function showAlert(message, type) { const alert = document.createElement('div'); alert.className = `alert alert-${type}`; alert.textContent = message; alert.style.position = 'fixed'; alert.style.top = '20px'; alert.style.right = '20px'; alert.style.zIndex = '9999'; alert.style.minWidth = '300px'; document.body.appendChild(alert); setTimeout(() => { if (document.body.contains(alert)) { document.body.removeChild(alert); } }, 5000); } // === ОПЕРАТОР: Веб-камера и WebRTC === function showOperatorWebcamButton() { const btn = document.getElementById('startOperatorWebcam'); if (btn) btn.style.display = ''; } function hideOperatorWebcamButton() { const btn = document.getElementById('startOperatorWebcam'); if (btn) btn.style.display = 'none'; } function startOperatorWebcam() { navigator.mediaDevices.getUserMedia({ video: true, audio: false }) .then(stream => { operatorWebcamStream = stream; const video = document.getElementById('operatorWebcam'); if (video) { video.srcObject = stream; video.style.display = ''; } startOperatorWebRTC(stream); }) .catch(err => { showAlert('danger', 'Ошибка доступа к веб-камере: ' + err); }); } function startOperatorWebRTC(stream) { operatorWebcamPeer = new RTCPeerConnection(rtcConfig); stream.getTracks().forEach(track => operatorWebcamPeer.addTrack(track, stream)); operatorWebcamPeer.onicecandidate = e => { if (e.candidate) { socket.emit('webrtc:ice-candidate', { candidate: e.candidate }); } }; operatorWebcamPeer.createOffer().then(offer => { return operatorWebcamPeer.setLocalDescription(offer); }).then(() => { socket.emit('webrtc:offer', { offer: operatorWebcamPeer.localDescription }); }); } socket.on('webrtc:answer', data => { if (operatorWebcamPeer) { operatorWebcamPeer.setRemoteDescription(new RTCSessionDescription(data.answer)); } }); socket.on('webrtc:ice-candidate', data => { if (operatorWebcamPeer && data.candidate) { operatorWebcamPeer.addIceCandidate(new RTCIceCandidate(data.candidate)); } }); // Показывать кнопку веб-камеры при принятии сессии socket.on('camera:response', data => { if (data.accepted) { showOperatorWebcamButton(); } else { hideOperatorWebcamButton(); } });