Files
Alfredv2/alfred/app/static/js/alfred-camera-popup.js
T
2026-10-26 01:38:47 +01:00

75 lines
3.0 KiB
JavaScript

/* ALFRED_CAMERA_POPUP_V1
* Utilise le pilote caméra MJPEG existant (alfred-camera.js), sans ouvrir
* un deuxième flux concurrent : ouverture = clic sur cameraToggle ;
* fermeture = arrêt immédiat du flux, libérant la webcam pour le MCP visage.
*/
(() => {
'use strict';
const trigger = document.getElementById('cameraPopupButton');
const overlay = document.getElementById('cameraPopup');
const closeButton = document.getElementById('cameraPopupClose');
const toggle = document.getElementById('cameraToggle');
if (!trigger || !overlay || !closeButton || !toggle) return;
let previousFocus = null;
function active() { return toggle.getAttribute('aria-pressed') === 'true'; }
function opened() { return !overlay.hidden; }
function setExpanded(value) { trigger.setAttribute('aria-expanded', String(value)); }
function openPopup() {
if (opened()) return;
previousFocus = document.activeElement;
overlay.hidden = false;
document.body.classList.add('alfred-camera-dialog-open');
setExpanded(true);
closeButton.focus();
// Réutilise la logique précédente pour accéder à /api/camera/stream.
if (!active()) toggle.click();
}
function closePopup() {
if (!opened()) return;
// L'ancienne implémentation du bouton ferme le flux MJPEG et retire src.
if (active()) toggle.click();
// Garde-fou contre un éventuel gestionnaire caméra incomplet.
const feed = document.getElementById('cameraFeed');
if (feed && feed.hasAttribute('src')) feed.removeAttribute('src');
overlay.hidden = true;
document.body.classList.remove('alfred-camera-dialog-open');
setExpanded(false);
if (previousFocus && document.contains(previousFocus) && typeof previousFocus.focus === 'function') {
previousFocus.focus();
} else {
trigger.focus();
}
previousFocus = null;
}
trigger.addEventListener('click', () => opened() ? closePopup() : openPopup());
closeButton.addEventListener('click', closePopup);
overlay.addEventListener('pointerdown', evt => {
if (evt.target === overlay) closePopup();
});
document.addEventListener('keydown', evt => {
if (!opened()) return;
if (evt.key === 'Escape') {
evt.preventDefault();
closePopup();
} else if (evt.key === 'Tab') {
// La page principale ne doit pas recevoir le focus derrière la modale.
const nodes = [...overlay.querySelectorAll('button:not([disabled]),a[href],input:not([disabled])')]
.filter(node => node.getClientRects().length);
if (!nodes.length) return;
const first = nodes[0], last = nodes[nodes.length - 1];
if (evt.shiftKey && document.activeElement === first) {
evt.preventDefault(); last.focus();
} else if (!evt.shiftKey && document.activeElement === last) {
evt.preventDefault(); first.focus();
}
}
});
// Le flux est également fermé par alfred-camera.js en cas de page masquée.
window.addEventListener('pagehide', () => { if (opened()) closePopup(); });
setExpanded(false);
})();