Note de ce sujet :
  • Moyenne : 0 (0 vote(s))
  • 1
  • 2
  • 3
  • 4
  • 5
Petit prog pour esp32
#44
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

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()">&times;</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>&nbsp;&nbsp;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>
Répondre


Messages dans ce sujet
Petit prog pour esp32 - par lucky - 27-05-2025, 09:04 PM
RE: Petit prog pour esp32 - par Sgb31 - 28-05-2025, 07:51 AM
RE: Petit prog pour esp32 - par lucky - 28-05-2025, 08:03 AM
RE: Petit prog pour esp32 - par Sgb31 - 28-05-2025, 09:52 AM
RE: Petit prog pour esp32 - par Serge111 - 28-05-2025, 03:09 PM
RE: Petit prog pour esp32 - par grostoto - 28-05-2025, 07:57 PM
RE: Petit prog pour esp32 - par lucky - 28-05-2025, 08:07 PM
RE: Petit prog pour esp32 - par grostoto - 28-05-2025, 09:48 PM
RE: Petit prog pour esp32 - par lucky - 28-05-2025, 10:03 PM
RE: Petit prog pour esp32 - par grostoto - 28-05-2025, 10:07 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 08:21 AM
RE: Petit prog pour esp32 - par Philmaz - 29-05-2025, 05:32 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 06:03 PM
RE: Petit prog pour esp32 - par Philmaz - 29-05-2025, 07:45 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 06:08 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 06:17 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 07:49 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 08:14 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 09:01 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 09:51 PM
RE: Petit prog pour esp32 - par Philmaz - 29-05-2025, 10:32 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 11:21 PM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 07:26 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 07:41 AM
RE: Petit prog pour esp32 - par grostoto - 30-05-2025, 08:02 AM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 08:19 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 08:26 AM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 08:29 AM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 09:01 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 09:02 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 09:00 AM
RE: Petit prog pour esp32 - par grostoto - 30-05-2025, 09:11 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 03:26 PM
RE: Petit prog pour esp32 - par PhDV61 - 30-05-2025, 09:12 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 09:26 AM
RE: Petit prog pour esp32 - par bernard62 - 30-05-2025, 12:12 PM
RE: Petit prog pour esp32 - par glu3 - 30-05-2025, 02:21 PM
RE: Petit prog pour esp32 - par pdunet - 30-05-2025, 02:29 PM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 05:33 PM
RE: Petit prog pour esp32 - par grostoto - 30-05-2025, 07:50 PM
RE: Petit prog pour esp32 - par 59jag - 30-05-2025, 10:03 PM
RE: Petit prog pour esp32 - par lucky - 31-05-2025, 08:03 AM
RE: Petit prog pour esp32 - par lucky - 31-05-2025, 08:04 AM
RE: Petit prog pour esp32 - par 59jag - 31-05-2025, 09:22 AM
RE: Petit prog pour esp32 - par Sgb31 - 31-05-2025, 09:35 AM
RE: Petit prog pour esp32 - par lucky - 31-05-2025, 12:01 PM
RE: Petit prog pour esp32 - par pdunet - Il y a 17 minutes

Atteindre :


Utilisateur(s) parcourant ce sujet : GPL, 3 visiteur(s)