31-05-2025, 09:22 AM
j ai modifie le zoom pour n importe quel orientation de l ecran et barres de zoom plus superposés sur les iframes
https://drive.google.com/file/d/12rkKS3T...p=drivesdk
https://drive.google.com/file/d/12rkKS3T...p=drivesdk
Code :
lucky<!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;
padding-top: 40px; /* Espace pour les contrôles */
}
.frame-wrapper {
width: 100%;
height: calc(100% - 40px); /* Ajuster la hauteur */
transform-origin: top left;
transition: transform 0.3s ease;
}
.zoom-controls {
position: absolute;
top: 5px; /* Reste en haut mais dans l'espace réservé */
right: 5px;
background: rgba(0,0,0,0.8);
padding: 5px;
border-radius: 3px;
z-index: 10;
display: flex;
gap: 5px;
}
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: min(5vw, 40px);
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: 30px;
flex-wrap: wrap;
justify-content: center;
}
.clear-btn {
background-color: #F44336;
flex: 1;
max-width: 200px;
}
.clear-btn:hover {
background-color: #D32F2F;
}
.cancel-btn {
background-color: #666;
}
.cancel-btn:hover {
background-color: #555;
}
</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="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="cancel-btn" onclick="closeConfigPopup()">Annuler</button>
</div>
<div class="config-actions">
<button class="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;
// Copie profonde des adresses pour éviter les modifications directes
addresses = JSON.parse(JSON.stringify(addresses));
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);
if (addresses.length === 0) {
addresses.push('');
}
renderIPList();
}
// Supprimer une adresse (popup)
function removeIPPopup(index) {
addresses.splice(index, 1);
if (addresses.length === 0) {
addresses.push('');
}
renderIPListPopup();
}
// Mettre à jour l'aperçu
function updateLayoutPreview(isPopup = false) {
const preview = document.getElementById(isPopup ? 'popupLayoutPreview': 'layoutPreview');
const count = addresses.filter(addr => addr.trim() !== '').length;
let layout = '';
if (count === 0) {
layout = 'Aucune interface configurée';
} else 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 {
const cols = Math.ceil(Math.sqrt(count));
const rows = Math.ceil(count / cols);
layout = `Grille ${cols}x${rows}`;
}
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 input = document.getElementById(`popupaddress${i}`);
if (input) {
const addressValue = input.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 = [''];
zoomLevels = {};
if (isPopupMode) {
renderIPListPopup();
} else {
renderIPList();
document.getElementById('welcomeMessage').style.display = 'block';
}
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"
};
}
// URL complète avec protocole
if (address.match(/^https?:\/\//)) {
return {
valid: true,
formatted: address,
display: address
};
}
// Pattern IP avec ou sans port
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
};
}
// Pattern domaine avec ou sans port et chemin
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
};
}
// Pattern nom simple avec port optionnel (pour réseaux locaux)
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}`);
if (frameContainer && frameContainer.parentElement) {
const optimalZoom = calculateOptimalZoom(frameContainer.parentElement);
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) {
autoFitAll();
}
},
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';
}
const frameNum = index + 1;
frameDiv.innerHTML = `
<span class="frame-label" title="${addressInfo.display}">${addressInfo.display}</span>
<div class="zoom-controls">
<button class="zoom-btn" onclick="zoom(${frameNum}, -0.1)">−</button>
<span class="zoom-value" id="zoom${frameNum}">100%</span>
<button class="zoom-btn" onclick="zoom(${frameNum}, 0.1)">+</button>
<button class="zoom-btn zoom-fit" onclick="autoFitFrame(${frameNum})" title="Ajuster à l'écran">FIT</button>
</div>
<div class="frame-wrapper" id="frame${frameNum}">
<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${frameNum}"
onerror="handleIframeError(${frameNum}, '${addressInfo.display}')"
></iframe>
</div>
<div class="iframe-error" id="error${frameNum}">
<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;
}
// Vérifier le chargement des iframes après un délai
setTimeout(checkIframeLoading, 2000);
}
// Retourner à la configuration
function showConfig() {
document.body.className = 'config-mode';
currentMode = 'config';
renderIPList();
}
// Fonctions de zoom
function zoom(frameNum, delta) {
const currentZoom = zoomLevels[frameNum] || 1;
zoomLevels[frameNum] = Math.max(0.2, Math.min(2, currentZoom + delta));
updateZoom(frameNum);
}
function updateZoom(frameNum) {
const frame = document.getElementById(`frame${frameNum}`);
const zoomDisplay = document.getElementById(`zoom${frameNum}`);
if (!frame || !zoomDisplay) return;
const scale = zoomLevels[frameNum] || 1;
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
console.log(`Iframe ${i} chargée avec succès`);
} catch (e) {
// Erreur de cross-origin, probablement bloqué
console.warn(`L'iframe ${i} pourrait être bloquée par des politiques de sécurité`);
}
});
iframe.addEventListener('error', function() {
handleIframeError(i, addresses[i - 1]);
});
}
}
}
}
// Gestion du redimensionnement de la fenêtre
let resizeTimeout;
window.addEventListener('resize', function() {
if (currentMode === 'dashboard' && autoFitEnabled) {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
autoFitAll();
}, 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();
}
});
</script>
</body>
</html>