bonjour
je voulais faire un peu pres la meme que ton html.
je donc récupérer le tiens pour faire quelques modif.
enregistrement de la config dans le localstorage.
ajustement du zoom par rapport taille ecran
https://drive.google.com/file/d/12HfRLeT...p=drivesdk
je voulais faire un peu pres la meme que ton html.
je donc récupérer le tiens pour faire quelques modif.
enregistrement de la config dans le localstorage.
ajustement du zoom par rapport taille ecran
https://drive.google.com/file/d/12HfRLeT...p=drivesdk
Code :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Multi-Interfaces</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background: #222;
}
/* Styles pour le mode configuration */
.config-mode {
background-color: #f5f5f5 !important;
}
.config-container {
display: none;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.config-mode .config-container {
display: block;
}
.config-panel {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 30px;
}
.ip-list {
margin-bottom: 20px;
}
.ip-group {
margin-bottom: 15px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 5px;
display: flex;
align-items: center;
gap: 10px;
}
.ip-number {
font-weight: bold;
color: #555;
min-width: 30px;
}
input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
input[type="text"]:focus {
outline: none;
border-color: #4CAF50;
}
.remove-btn {
background-color: #f44336;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.remove-btn:hover {
background-color: #d32f2f;
}
.add-btn {
background-color: #2196F3;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-bottom: 20px;
width: 100%;
}
.add-btn:hover {
background-color: #1976D2;
}
.button-group {
margin-top: 30px;
display: flex;
gap: 10px;
justify-content: center;
}
button {
padding: 12px 30px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.save-btn {
background-color: #4CAF50;
color: white;
}
.save-btn:hover {
background-color: #45a049;
}
.message {
margin-top: 20px;
padding: 10px;
border-radius: 5px;
text-align: center;
display: none;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.layout-preview {
margin-top: 20px;
padding: 15px;
background-color: #e3f2fd;
border-radius: 5px;
text-align: center;
font-size: 14px;
color: #1976D2;
}
.help-text {
font-size: 12px;
color: #666;
margin-top: 15px;
padding: 10px;
background-color: #f0f0f0;
border-radius: 5px;
line-height: 1.6;
}
/* Styles pour le mode dashboard */
.dashboard-container {
display: none;
height: 100vh;
gap: 2px;
background: #222;
}
.dashboard-mode .dashboard-container {
display: grid;
}
.frame-container {
position: relative;
overflow: hidden;
background: white;
}
.frame-wrapper {
width: 100%;
height: 100%;
transform-origin: top left;
transition: transform 0.3s ease;
}
iframe {
width: 100%;
height: 100%;
border: none;
background: white;
}
/* Icône de configuration fixe */
.config-icon {
position: fixed;
top: 5px;
left: 5px;
z-index: 1000;
color: rgba(255, 255, 255, 0.8);
font-size: 5vw;
cursor: pointer;
transition: all 0.3s ease;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
user-select: none;
display: none;
}
.dashboard-mode .config-icon {
display: block;
}
.config-icon:hover {
color: #FF9800;
transform: rotate(45deg) scale(1.1);
}
/* Fenêtre popup de configuration */
.config-popup {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 2000;
overflow-y: auto;
}
.config-popup-content {
background: white;
margin: 50px auto;
padding: 30px;
border-radius: 10px;
max-width: 800px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
position: relative;
animation: popupSlideIn 0.3s ease-out;
}
@keyframes popupSlideIn {
from {
opacity: 0;
transform: translateY(-50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.close-popup {
position: absolute;
top: 15px;
right: 20px;
font-size: 30px;
color: #999;
cursor: pointer;
transition: color 0.3s;
}
.close-popup:hover {
color: #333;
}
/* Contrôles de zoom */
.zoom-controls {
position: absolute;
top: 5px;
right: 5px;
background: rgba(0,0,0,0.8);
padding: 5px;
border-radius: 3px;
z-index: 10;
display: flex;
gap: 5px;
}
.zoom-btn {
width: 25px;
height: 25px;
border: none;
background: #444;
color: white;
cursor: pointer;
border-radius: 3px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.zoom-btn:hover {
background: #666;
}
.zoom-fit {
background: #2196F3;
width: auto;
min-width: 25px;
padding: 0 8px;
font-size: 11px;
}
.zoom-fit:hover {
background: #1976D2;
}
.zoom-value {
color: white;
font-family: Arial, sans-serif;
font-size: 12px;
min-width: 45px;
text-align: center;
display: flex;
align-items: center;
}
/* Label optionnel */
.frame-label {
position: absolute;
top: 5px;
left: 5px;
background: rgba(0,0,0,0.7);
color: white;
padding: 5px 10px;
font-family: Arial, sans-serif;
font-size: 12px;
border-radius: 3px;
z-index: 10;
pointer-events: none;
max-width: 70%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Message d'accueil */
.welcome-message {
text-align: center;
padding: 40px;
background-color: #e3f2fd;
border-radius: 10px;
margin-bottom: 30px;
}
.welcome-message h2 {
color: #1976D2;
margin-bottom: 10px;
}
/* Message d'erreur iframe */
.iframe-error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #f8d7da;
color: #721c24;
padding: 20px;
border-radius: 8px;
text-align: center;
max-width: 80%;
z-index: 20;
display: none;
}
.iframe-error h3 {
margin: 0 0 10px 0;
font-size: 16px;
}
.iframe-error p {
margin: 5px 0;
font-size: 14px;
}
.iframe-error code {
background: rgba(0,0,0,0.1);
padding: 2px 5px;
border-radius: 3px;
font-size: 12px;
}
/* Boutons d'action de configuration */
.config-actions {
display: flex;
gap: 10px;
margin-top: 20px;
flex-wrap: wrap;
}
.clear-btn {
background-color: #F44336;
}
.clear-btn:hover {
background-color: #D32F2F;
}
</style>
</head>
<body class="config-mode">
<!-- Icône de configuration fixe -->
<div class="config-icon" onclick="openConfigPopup()" title="Configuration">
⚙️
</div>
<!-- Mode Configuration -->
<div class="config-container">
<div class="config-panel">
<h1>Dashboard Multi-Interfaces</h1>
<div id="welcomeMessage" class="welcome-message">
<h2>Bienvenue !</h2>
<p>
Configurez vos adresses ci-dessous pour commencer.
</p>
</div>
<div id="ipList" class="ip-list"></div>
<button class="add-btn" onclick="addIP()">+ Ajouter une interface</button>
<div class="help-text">
<strong>Formats acceptés :</strong><br>
• IP : 192.168.1.100<br>
• Domaine : example.com<br>
• Sous-domaine : app.example.com<br>
• URL complète : https://example.com/dashboard<br>
• Port personnalisé : 192.168.1.100:8080 ou example.com:3000
</div>
<div id="layoutPreview" class="layout-preview"></div>
<div class="button-group">
<button class="save-btn" onclick="saveAndShowDashboard()">Sauvegarder et afficher</button>
</div>
<div class="config-actions">
<button class="save-btn clear-btn" onclick="clearConfig()">?️ Effacer tout</button>
</div>
<div id="message" class="message"></div>
</div>
</div>
<!-- Popup de configuration -->
<div class="config-popup" id="configPopup">
<div class="config-popup-content">
<span class="close-popup" onclick="closeConfigPopup()">×</span>
<h1>Configuration Dashboard</h1>
<div id="popupIpList" class="ip-list"></div>
<button class="add-btn" onclick="addIPPopup()">+ Ajouter une interface</button>
<div class="help-text">
<strong>Formats acceptés :</strong><br>
• IP : 192.168.1.100<br>
• Domaine : example.com<br>
• Sous-domaine : app.example.com<br>
• URL complète : https://example.com/dashboard<br>
• Port personnalisé : 192.168.1.100:8080 ou example.com:3000
</div>
<div id="popupLayoutPreview" class="layout-preview"></div>
<div class="button-group">
<button class="save-btn" onclick="saveConfigFromPopup()">Sauvegarder et appliquer</button>
<button class="save-btn" style="background-color: #666;" onclick="closeConfigPopup()">Annuler</button>
</div>
<div class="config-actions">
<button class="save-btn clear-btn" onclick="clearConfig()">?️ Effacer tout</button>
</div>
<div id="popupMessage" class="message"></div>
</div>
</div>
<!-- Mode Dashboard -->
<div id="dashboardContainer" class="dashboard-container"></div>
<script>
// Configuration des adresses - Chargée depuis localStorage
let addresses = [];
let zoomLevels = {};
let currentMode = 'config';
let autoFitEnabled = true;
let isPopupMode = false;
// Clé de stockage localStorage
const STORAGE_KEY = 'dashboard_config';
// Initialisation
window.onload = function() {
loadConfig();
if (addresses.length > 0 && addresses[0] !== '') {
document.getElementById('welcomeMessage').style.display = 'none';
// Aller directement au dashboard si configuré
showDashboard();
} else {
renderIPList();
}
};
// Charger la configuration depuis localStorage
function loadConfig() {
try {
const savedConfig = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
if (Array.isArray(savedConfig) && savedConfig.length > 0) {
addresses = savedConfig;
} else {
addresses = [''];
}
} catch (e) {
console.error('Erreur lors du chargement de la configuration:', e);
addresses = [''];
}
}
// Sauvegarder la configuration dans localStorage
function saveConfig() {
try {
const cleanAddresses = addresses.filter(addr => addr.trim() !== '');
localStorage.setItem(STORAGE_KEY, JSON.stringify(cleanAddresses));
return true;
} catch (e) {
console.error('Erreur lors de la sauvegarde:', e);
showMessage('Erreur lors de la sauvegarde de la configuration', 'error');
return false;
}
}
// Ouvrir la popup de configuration
function openConfigPopup() {
isPopupMode = true;
addresses = [...addresses]; // Copie pour éviter les modifications directes
renderIPListPopup();
document.getElementById('configPopup').style.display = 'block';
document.body.style.overflow = 'hidden'; // Désactiver le scroll du body
}
// Fermer la popup de configuration
function closeConfigPopup() {
isPopupMode = false;
document.getElementById('configPopup').style.display = 'none';
document.body.style.overflow = 'auto'; // Réactiver le scroll du body
loadConfig(); // Recharger la config originale
}
// Afficher la liste des adresses (mode principal)
function renderIPList() {
const container = document.getElementById('ipList');
renderIPListInContainer(container, false);
}
// Afficher la liste des adresses (popup)
function renderIPListPopup() {
const container = document.getElementById('popupIpList');
renderIPListInContainer(container, true);
}
// Fonction générique pour rendre la liste d'IPs
function renderIPListInContainer(container, isPopup) {
container.innerHTML = '';
addresses.forEach((address, index) => {
const div = document.createElement('div');
div.className = 'ip-group';
div.innerHTML = `
<span class="ip-number">${index + 1}.</span>
<input type="text" id="${isPopup ? 'popup': ''}address${index}" value="${address}" placeholder="Ex: 192.168.1.100, example.com, https://app.example.com">
${addresses.length > 1 ? `<button class="remove-btn" onclick="${isPopup ? 'removeIPPopup': 'removeIP'}(${index})">Supprimer</button>`: ''}
`;
container.appendChild(div);
});
updateLayoutPreview(isPopup);
}
// Ajouter une adresse
function addIP() {
addresses.push('');
renderIPList();
}
// Ajouter une adresse (popup)
function addIPPopup() {
addresses.push('');
renderIPListPopup();
}
// Supprimer une adresse
function removeIP(index) {
addresses.splice(index, 1);
renderIPList();
}
// Supprimer une adresse (popup)
function removeIPPopup(index) {
addresses.splice(index, 1);
renderIPListPopup();
}
// Mettre à jour l'aperçu
function updateLayoutPreview(isPopup = false) {
const preview = document.getElementById(isPopup ? 'popupLayoutPreview': 'layoutPreview');
const count = addresses.length;
let layout = '';
if (count === 1) {
layout = 'Plein écran';
} else if (count === 2) {
layout = '2 colonnes côte à côte';
} else if (count === 3) {
layout = '1 grande vue à gauche + 2 petites à droite';
} else if (count === 4) {
layout = 'Grille 2x2';
} else if (count <= 6) {
layout = 'Grille 2x3';
} else if (count <= 9) {
layout = 'Grille 3x3';
} else {
layout = `Grille ${Math.ceil(Math.sqrt(count))}x${Math.ceil(count / Math.ceil(Math.sqrt(count)))}`;
}
preview.innerHTML = `<strong>Disposition :</strong> ${count} interface${count > 1 ? 's': ''} - ${layout}`;
}
// Sauvegarder la configuration depuis la popup
function saveConfigFromPopup() {
// Récupérer et valider les adresses depuis la popup
const newAddresses = [];
for (let i = 0; i < addresses.length; i++) {
const addressValue = document.getElementById(`popupaddress${i}`).value.trim();
if (addressValue) {
const validation = validateAndFormatAddress(addressValue);
if (!validation.valid) {
showMessage(`Adresse n°${i + 1} : ${validation.error}`, 'error', true);
return;
}
newAddresses.push(addressValue);
}
}
if (newAddresses.length === 0) {
showMessage('Ajoutez au moins une adresse valide', 'error', true);
return;
}
addresses = newAddresses;
if (saveConfig()) {
showMessage('Configuration sauvegardée avec succès !', 'success', true);
setTimeout(() => {
closeConfigPopup();
showDashboard();
}, 1000);
}
}
// Sauvegarder et afficher le dashboard (mode principal)
function saveAndShowDashboard() {
// Récupérer et valider les adresses
const newAddresses = [];
for (let i = 0; i < addresses.length; i++) {
const addressInput = document.getElementById(`address${i}`);
const addressValue = addressInput ? addressInput.value.trim(): addresses[i];
if (addressValue) {
const validation = validateAndFormatAddress(addressValue);
if (!validation.valid) {
showMessage(`Adresse n°${i + 1} : ${validation.error}`, 'error');
return;
}
newAddresses.push(addressValue);
}
}
if (newAddresses.length === 0) {
showMessage('Ajoutez au moins une adresse valide', 'error');
return;
}
addresses = newAddresses;
if (saveConfig()) {
showMessage('Configuration sauvegardée !', 'success');
setTimeout(showDashboard, 500);
}
}
// Effacer toute la configuration
function clearConfig() {
if (confirm('Êtes-vous sûr de vouloir effacer toute la configuration ?')) {
localStorage.removeItem(STORAGE_KEY);
addresses = [''];
if (isPopupMode) {
renderIPListPopup();
} else {
renderIPList();
}
showMessage('Configuration effacée', 'success', isPopupMode);
}
}
// Valider et formater une adresse
function validateAndFormatAddress(address) {
address = address.trim();
if (!address) {
return {
valid: false,
error: "L'adresse ne peut pas être vide"
};
}
if (address.match(/^https?:\/\//)) {
return {
valid: true,
formatted: address,
display: address
};
}
const ipPattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?::\d{1,5})?$/;
if (ipPattern.test(address)) {
return {
valid: true,
formatted: `http://${address}`,
display: address
};
}
const domainPattern = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(:\d{1,5})?(\/.*)?$/;
const localhostPattern = /^localhost(:\d{1,5})?(\/.*)?$/;
if (domainPattern.test(address) || localhostPattern.test(address)) {
return {
valid: true,
formatted: `http://${address}`,
display: address
};
}
if (/^[a-zA-Z0-9-]+(:\d{1,5})?(\/.*)?$/.test(address)) {
return {
valid: true,
formatted: `http://${address}`,
display: address
};
}
return {
valid: false,
error: "Format d'adresse non valide"
};
}
// Afficher un message
function showMessage(text, type, inPopup = false) {
const messageEl = document.getElementById(inPopup ? 'popupMessage': 'message');
messageEl.textContent = text;
messageEl.className = 'message ' + type;
messageEl.style.display = 'block';
setTimeout(() => {
messageEl.style.display = 'none';
}, 3000);
}
// Calculer la disposition
function calculateGridLayout(count) {
if (count === 1) return {
cols: 1,
rows: 1,
special: 'single'
};
if (count === 2) return {
cols: 2,
rows: 1,
special: 'two-columns'
};
if (count === 3) return {
cols: 2,
rows: 2,
special: 'three-special'
};
if (count === 4) return {
cols: 2,
rows: 2,
special: 'grid'
};
const cols = Math.ceil(Math.sqrt(count));
const rows = Math.ceil(count / cols);
return {
cols,
rows,
special: 'grid'
};
}
function calculateOptimalZoom(frameContainer) {
const containerWidth = frameContainer.clientWidth;
// Utiliser la largeur actuelle en tenant compte de l'orientation
const referenceWidth = Math.max(window.innerWidth, window.innerHeight);
const optimalZoom = containerWidth / referenceWidth;
return Math.max(0.2, Math.min(2, optimalZoom));
}
// Appliquer le zoom automatique à une iframe
function autoFitFrame(frameNum) {
const frameContainer = document.querySelector(`#frame${frameNum}`).parentElement;
const optimalZoom = calculateOptimalZoom(frameContainer);
zoomLevels[frameNum] = optimalZoom;
updateZoom(frameNum);
}
// Appliquer le zoom automatique à toutes les iframes
function autoFitAll() {
for (let i = 1; i <= addresses.length; i++) {
autoFitFrame(i);
}
}
// Afficher le dashboard
function showDashboard() {
// Valider les adresses
const validatedAddresses = [];
for (let i = 0; i < addresses.length; i++) {
const validation = validateAndFormatAddress(addresses[i]);
if (!validation.valid) {
showMessage(`Adresse n°${i + 1} : ${validation.error}`, 'error');
return;
}
validatedAddresses.push(validation);
}
// Construire le dashboard avec les adresses formatées
buildDashboard(validatedAddresses);
// Basculer en mode dashboard
document.body.className = 'dashboard-mode';
currentMode = 'dashboard';
// Appliquer le zoom automatique après un délai
setTimeout(() => {
if (autoFitEnabled) {
for (let i = 1; i <= addresses.length; i++) {
autoFitFrame(i);
}
}
},
500);
}
// Construire le dashboard
function buildDashboard(validatedAddresses) {
const container = document.getElementById('dashboardContainer');
const layout = calculateGridLayout(validatedAddresses.length);
// Appliquer les styles de grille
if (layout.special === 'single') {
container.style.gridTemplateColumns = '1fr';
container.style.gridTemplateRows = '1fr';
} else if (layout.special === 'two-columns') {
container.style.gridTemplateColumns = '1fr 1fr';
container.style.gridTemplateRows = '1fr';
} else if (layout.special === 'three-special') {
container.style.gridTemplateColumns = '1fr 1fr';
container.style.gridTemplateRows = '1fr 1fr';
} else {
container.style.gridTemplateColumns = `repeat(${layout.cols}, 1fr)`;
container.style.gridTemplateRows = `repeat(${layout.rows}, 1fr)`;
}
// Créer les conteneurs
container.innerHTML = '';
validatedAddresses.forEach((addressInfo, index) => {
const frameDiv = document.createElement('div');
frameDiv.className = 'frame-container';
// Style spécial pour 3 interfaces
if (layout.special === 'three-special' && index === 0) {
frameDiv.style.gridRow = 'span 2';
}
frameDiv.innerHTML = `
<span class="frame-label" title="${addressInfo.display}">${addressInfo.display}</span>
<div class="zoom-controls">
<button class="zoom-btn" onclick="zoom(${index + 1}, -0.1)">−</button>
<span class="zoom-value" id="zoom${index + 1}">100%</span>
<button class="zoom-btn" onclick="zoom(${index + 1}, 0.1)">+</button>
<button class="zoom-btn zoom-fit" onclick="autoFitFrame(${index + 1})" title="Ajuster à l'écran">FIT</button>
</div>
<div class="frame-wrapper" id="frame${index + 1}">
<iframe
src="${addressInfo.formatted}"
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-modals allow-downloads allow-presentation allow-top-navigation"
referrerpolicy="no-referrer"
id="iframe${index + 1}"
onerror="handleIframeError(${index + 1}, '${addressInfo.display}')"
></iframe>
</div>
<div class="iframe-error" id="error${index + 1}">
<h3>⚠️ Impossible de charger ${addressInfo.display}</h3>
<p>Cette interface ne peut pas être affichée dans une iframe.</p>
<p><small>Pour Home Assistant, ajoutez dans configuration.yaml :</small></p>
<code>http:<br> use_x_frame_options: false</code>
<p style="margin-top: 15px;">
<a href="${addressInfo.formatted}" target="_blank" style="color: #0056b3;">Ouvrir dans un nouvel onglet →</a>
</p>
</div>
`;
container.appendChild(frameDiv);
});
// Initialiser les niveaux de zoom
zoomLevels = {};
for (let i = 1; i <= validatedAddresses.length; i++) {
zoomLevels[i] = 1;
}
}
// Retourner à la configuration
function showConfig() {
document.body.className = 'config-mode';
currentMode = 'config';
renderIPList();
}
// Fonctions de zoom
function zoom(frameNum, delta) {
zoomLevels[frameNum] = Math.max(0.2, Math.min(2, zoomLevels[frameNum] + delta));
updateZoom(frameNum);
}
function updateZoom(frameNum) {
const frame = document.getElementById(`frame${frameNum}`);
const zoomDisplay = document.getElementById(`zoom${frameNum}`);
const scale = zoomLevels[frameNum];
frame.style.transform = `scale(${scale})`;
frame.style.width = `${100 / scale}%`;
frame.style.height = `${100 / scale}%`;
zoomDisplay.textContent = `${Math.round(scale * 100)}%`;
}
// Gestion des erreurs d'iframe
function handleIframeError(frameNum, address) {
console.error(`Erreur de chargement pour l'iframe ${frameNum}: ${address}`);
const errorDiv = document.getElementById(`error${frameNum}`);
if (errorDiv) {
errorDiv.style.display = 'block';
}
}
// Détection des erreurs de chargement après un délai
function checkIframeLoading() {
if (currentMode === 'dashboard') {
for (let i = 1; i <= addresses.length; i++) {
const iframe = document.getElementById(`iframe${i}`);
if (iframe) {
iframe.addEventListener('load', function() {
try {
// Tenter d'accéder au document pour vérifier si c'est bloqué
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// Si on arrive ici, l'iframe est chargée correctement
} catch (e) {
// Erreur de cross-origin, probablement bloqué
console.warn(`L'iframe ${i} pourrait être bloquée par des politiques de sécurité`);
}
});
}
}
}
}
// Gestion du redimensionnement de la fenêtre
window.addEventListener('resize', function() {
if (currentMode === 'dashboard' && autoFitEnabled) {
// Réappliquer le zoom automatique après redimensionnement
setTimeout(() => {
for (let i = 1; i <= addresses.length; i++) {
autoFitFrame(i);
}
},
100);
}
});
// Fermer la popup avec Escape
window.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && document.getElementById('configPopup').style.display === 'block') {
closeConfigPopup();
}
});
// Fermer la popup en cliquant en dehors
document.getElementById('configPopup').addEventListener('click', function(e) {
if (e.target === this) {
closeConfigPopup();
}
});
// Appeler la vérification après le chargement du dashboard
setTimeout(checkIframeLoading, 1000);
</script>
</body>
</html>