75 lines
3.0 KiB
JavaScript
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);
|
|
})();
|