Mudanças entre as edições de "Widget:MapViewer.js"

De Wiki Gla
Ir para navegação Ir para pesquisar
 
(16 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
<includeonly><div id="mapa-viewer-<!--{$id|escape:'quotes'}-->" style="width:<!--{$largura|escape:'quotes'|default:'100%'}-->; height:<!--{$altura|escape:'quotes'|default:'500px'}-->; background:#0f172a; border-radius:12px; overflow:hidden; position:relative;">
<includeonly><div id="mapa-viewer-<!--{$id|escape:'quotes'|default:'mapa1'}-->" style="width:<!--{$largura|escape:'quotes'|default:'100%'}-->; height:<!--{$altura|escape:'quotes'|default:'500px'}-->; background:#0f172a; border-radius:12px; overflow:hidden; position:relative;">
     <div style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#64748b; text-align:center;">
     <div style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#64748b; text-align:center;">
         <div>🗺️ Carregando mapa...</div>
         🗺️ Carregando mapa...
        <div style="font-size:12px; margin-top:8px;"><!--{$nome|escape:'quotes'|default:'Mapa'}--></div>
     </div>
     </div>
</div>
</div>


<script>
<script>
console.log("Iniciando mapViewr");
// @noescape
// @noescape
(function() {
(function() {
     var id = '<!--{$id|escape:'quotes'}-->';
     var id = '<!--{$id|escape:'quotes'|default:'mapa1'}-->';
     var containerId = 'mapa-viewer-' + id;
     var container = document.getElementById('mapa-viewer-' + id);
    var container = document.getElementById(containerId);
     if (!container) return;
     if (!container) return;
      
      
     // PEGAR O JSON - Usando a sintaxe correta!
     // JSON vem URL encoded para não quebrar o parser
     var jsonString = '{{#invoke:MapaJson|process|json=<!--{$json}-->}}';
     var jsonEncoded = '<!--{$json|escape:'quotes'}-->';
      
     var jsonString = decodeURIComponent(jsonEncoded);
    if (!jsonString || jsonString === '') {
        container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">❌ Configuração não encontrada</div>';
        return;
    }
      
      
     var mapConfig;
     var mapConfig;
     try {
     try {
console.log("mapConfig");
         mapConfig = JSON.parse(jsonString);
         mapConfig = JSON.parse(jsonString);
        console.log(mapConfig);
     } catch(e) {
     } catch(e) {
         container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">❌ Erro no JSON: ' + e.message + '</div>';
         container.innerHTML = '<div style="padding:20px; text-align:center; color:#ef4444;">❌ Erro no JSON: ' + e.message + '</div>';
        console.error('JSON inválido:', jsonString);
         return;
         return;
     }
     }
Linha 40: Linha 29:
     }
     }
      
      
     // Iniciar o viewer
     // Iniciar o visualizador
     iniciarMapaViewer(containerId, mapConfig);
     iniciarMapaViewer(container, mapConfig);
      
      
     function iniciarMapaViewer(containerId, config) {
     function iniciarMapaViewer(container, config) {
        var container = document.getElementById(containerId);
        if (!container) return;
       
         container.innerHTML = '';
         container.innerHTML = '';
         container.style.position = 'relative';
         container.style.position = 'relative';
         container.style.overflow = 'hidden';
         container.style.overflow = 'hidden';
          
          
         // Toolbar superior
         // Toolbar
         var toolbar = document.createElement('div');
         var toolbar = document.createElement('div');
         toolbar.style.cssText = 'position:absolute; top:10px; left:10px; right:10px; z-index:100; display:flex; justify-content:space-between; gap:8px; flex-wrap:wrap;';
         toolbar.style.cssText = 'position:absolute; top:10px; left:10px; right:10px; z-index:100; display:flex; justify-content:space-between; gap:8px; flex-wrap:wrap;';
         toolbar.innerHTML = `
         toolbar.innerHTML = '<div style="display:flex; gap:5px; background:rgba(0,0,0,0.7); padding:5px 10px; border-radius:30px;">' +
            <div style="display:flex; gap:5px; background:rgba(0,0,0,0.7); padding:5px 10px; border-radius:30px;">
            '<button id="zoom-in-' + id + '" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">+</button>' +
                <button class="mapa-zoom-in" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">+</button>
            '<button id="zoom-out-' + id + '" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">-</button>' +
                <button class="mapa-zoom-out" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">-</button>
            '<button id="reset-' + id + '" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">⟳</button>' +
                <button class="mapa-reset" style="background:#334155; border:none; color:white; width:32px; height:32px; border-radius:50%; cursor:pointer;">⟳</button>
             '</div>' +
             </div>
             '<div style="background:rgba(0,0,0,0.7); padding:5px 15px; border-radius:30px; color:white; font-size:12px;"><span id="floor-name-' + id + '">' + (config.layers[0]?.name || 'Mapa') + '</span></div>' +
             <div style="background:rgba(0,0,0,0.7); padding:5px 15px; border-radius:30px; color:white; font-size:12px;">
             '<div id="zoom-level-' + id + '" style="background:rgba(0,0,0,0.7); padding:5px 12px; border-radius:30px; color:#a5b4fc; font-size:12px;">100%</div>';
                <span class="mapa-floor-name">${config.layers[0]?.name || 'Mapa'}</span>
            </div>
             <div style="background:rgba(0,0,0,0.7); padding:5px 12px; border-radius:30px; color:#a5b4fc; font-size:12px;" class="mapa-zoom-level">100%</div>
        `;
          
          
         // Viewport
         // Viewport
         var viewport = document.createElement('div');
         var viewport = document.createElement('div');
        viewport.className = 'mapa-viewport';
         viewport.style.cssText = 'width:100%; height:100%; overflow:auto; cursor:grab; background:#0f172a;';
         viewport.style.cssText = 'width:100%; height:100%; overflow:auto; cursor:grab; background:#0f172a;';
        var camadasDiv = document.createElement('div');
        camadasDiv.style.cssText = 'position:relative; min-width:100%; min-height:100%;';
        viewport.appendChild(camadasDiv);
          
          
         var layersContainer = document.createElement('div');
        // Navegação
         layersContainer.className = 'mapa-layers';
         var navDiv = document.createElement('div');
         layersContainer.style.cssText = 'position:relative; min-width:100%; min-height:100%;';
         navDiv.style.cssText = 'position:absolute; bottom:20px; left:20px; z-index:100; display:flex; gap:8px; background:rgba(0,0,0,0.7); padding:8px; border-radius:40px;';
        viewport.appendChild(layersContainer);
         navDiv.innerHTML = '<button id="prev-' + id + '" style="background:#334155; border:none; color:white; width:36px; height:36px; border-radius:50%; cursor:pointer;">▲</button>' +
            '<button id="next-' + id + '" style="background:#334155; border:none; color:white; width:36px; height:36px; border-radius:50%; cursor:pointer;">▼</button>';
          
          
         // Coordenadas
         // Coordenadas
Linha 80: Linha 65:
         coordsDiv.style.cssText = 'position:absolute; bottom:10px; right:10px; background:rgba(0,0,0,0.6); padding:4px 10px; border-radius:20px; color:#a5b4fc; font-size:10px; font-family:monospace;';
         coordsDiv.style.cssText = 'position:absolute; bottom:10px; right:10px; background:rgba(0,0,0,0.6); padding:4px 10px; border-radius:20px; color:#a5b4fc; font-size:10px; font-family:monospace;';
         coordsDiv.textContent = '📍 0, 0';
         coordsDiv.textContent = '📍 0, 0';
       
        // Navegação
        var navDiv = document.createElement('div');
        navDiv.style.cssText = 'position:absolute; bottom:20px; left:20px; z-index:100; display:flex; gap:8px; background:rgba(0,0,0,0.7); padding:8px; border-radius:40px;';
        navDiv.innerHTML = `
            <button class="mapa-prev-floor" style="background:#334155; border:none; color:white; width:36px; height:36px; border-radius:50%; cursor:pointer;">▲</button>
            <button class="mapa-next-floor" style="background:#334155; border:none; color:white; width:36px; height:36px; border-radius:50%; cursor:pointer;">▼</button>
        `;
          
          
         container.appendChild(toolbar);
         container.appendChild(toolbar);
Linha 95: Linha 72:
          
          
         // Variáveis
         // Variáveis
         var currentZoom = config.mapConfig.defaultZoom || 1;
         var zoomAtual = config.mapConfig.defaultZoom || 1;
         var currentFloor = config.mapConfig.initialFloor || 0;
         var andarAtual = config.mapConfig.initialFloor || 0;
         var minZoom = config.mapConfig.minZoom || 0.5;
         var zoomMin = config.mapConfig.minZoom || 0.5;
         var maxZoom = config.mapConfig.maxZoom || 3;
         var zoomMax = config.mapConfig.maxZoom || 3;
         var zoomStep = config.mapConfig.zoomStep || 0.1;
         var zoomPasso = config.mapConfig.zoomStep || 0.1;
         var layers = [];
         var camadas = [];
         var markers = [];
         var marcadores = [];
         var isPanning = false;
         var arrastando = false;
         var panStart = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 };
         var arrasteInicio = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 };
       
        var floorNameSpan = toolbar.querySelector('.mapa-floor-name');
        var zoomLevelSpan = toolbar.querySelector('.mapa-zoom-level');
          
          
         function renderizarCamadas() {
         function renderizarCamadas() {
             layersContainer.innerHTML = '';
             camadasDiv.innerHTML = '';
             layers = [];
             camadas = [];
             markers = [];
             marcadores = [];
              
              
             var floorExists = false;
            if (!config.layers || config.layers.length === 0) return;
           
             var andarExiste = false;
             for (var i = 0; i < config.layers.length; i++) {
             for (var i = 0; i < config.layers.length; i++) {
                 if (config.layers[i].id === currentFloor) floorExists = true;
                 if (config.layers[i].id === andarAtual) andarExiste = true;
            }
            if (!floorExists && config.layers.length > 0) {
                currentFloor = config.layers[0].id;
             }
             }
            if (!andarExiste && config.layers.length > 0) andarAtual = config.layers[0].id;
              
              
             var floorAtual = null;
             var andarInfo = null;
             for (var i = 0; i < config.layers.length; i++) {
             for (var i = 0; i < config.layers.length; i++) {
                 if (config.layers[i].id === currentFloor) floorAtual = config.layers[i];
                 if (config.layers[i].id === andarAtual) andarInfo = config.layers[i];
            }
            if (floorAtual && floorNameSpan) {
                floorNameSpan.textContent = floorAtual.name;
             }
             }
            var nomeSpan = document.getElementById('floor-name-' + id);
            if (nomeSpan && andarInfo) nomeSpan.textContent = andarInfo.name;
              
              
             for (var i = 0; i < config.layers.length; i++) {
             for (var i = 0; i < config.layers.length; i++) {
Linha 134: Linha 107:
                 divLayer.className = 'mapa-layer';
                 divLayer.className = 'mapa-layer';
                 divLayer.setAttribute('data-floor', layer.id);
                 divLayer.setAttribute('data-floor', layer.id);
                 divLayer.style.display = layer.id === currentFloor ? 'block' : 'none';
                 divLayer.style.display = layer.id === andarAtual ? 'block' : 'none';
                 divLayer.style.position = 'absolute';
                 divLayer.style.position = 'absolute';
                 divLayer.style.top = '0';
                 divLayer.style.top = '0';
Linha 143: Linha 116:
                 var img = document.createElement('img');
                 var img = document.createElement('img');
                 img.style.display = 'block';
                 img.style.display = 'block';
                 img.style.transform = 'scale(' + currentZoom + ')';
                 img.style.transform = 'scale(' + zoomAtual + ')';
                 img.style.transformOrigin = '0 0';
                 img.style.transformOrigin = '0 0';
                 img.src = layer.imagePath || '';
                 img.src = layer.imagePath || '';
               
                 img.onload = function() { camadasDiv.style.width = this.width + 'px'; camadasDiv.style.height = this.height + 'px'; };
                 img.onload = function() {
                 img.onerror = function() { this.src = 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22800%22 height=%22600%22%3E%3Crect width=%22100%25%22 height=%22100%25%22 fill=%22%23334155%22/%3E%3Ctext x=%2250%25%22 y=%2250%25%22 text-anchor=%22middle%22 fill=%22white%22%3ESem imagem%3C/text%3E%3C/svg%3E'; };
                    layersContainer.style.width = this.width + 'px';
                    layersContainer.style.height = this.height + 'px';
                };
               
                 img.onerror = (function(layerName) {
                    return function() {
                        var canvas = document.createElement('canvas');
                        canvas.width = 800;
                        canvas.height = 600;
                        var ctx = canvas.getContext('2d');
                        ctx.fillStyle = '#334155';
                        ctx.fillRect(0, 0, canvas.width, canvas.height);
                        ctx.fillStyle = 'white';
                        ctx.font = '20px Arial';
                        ctx.fillText(layerName || 'Andar', 50, 100);
                        this.src = canvas.toDataURL();
                    };
                })(layer.name);
               
                 divLayer.appendChild(img);
                 divLayer.appendChild(img);
                  
                  
Linha 182: Linha 136:
                         var markerDiv = criarMarcador(marker, layer.id);
                         var markerDiv = criarMarcador(marker, layer.id);
                         markersDiv.appendChild(markerDiv);
                         markersDiv.appendChild(markerDiv);
                         markers.push({ el: markerDiv, data: marker });
                         marcadores.push({ el: markerDiv, data: marker });
                     }
                     }
                 }
                 }
               
                 divLayer.appendChild(markersDiv);
                 divLayer.appendChild(markersDiv);
                 layersContainer.appendChild(divLayer);
                 camadasDiv.appendChild(divLayer);
                 layers.push(divLayer);
                 camadas.push(divLayer);
             }
             }
         }
         }
Linha 199: Linha 152:
             div.setAttribute('data-x', marker.x);
             div.setAttribute('data-x', marker.x);
             div.setAttribute('data-y', marker.y);
             div.setAttribute('data-y', marker.y);
           
             div.style.position = 'absolute';
             div.style.position = 'absolute';
             div.style.left = (marker.x * currentZoom) + 'px';
             div.style.left = (marker.x * zoomAtual) + 'px';
             div.style.top = (marker.y * currentZoom) + 'px';
             div.style.top = (marker.y * zoomAtual) + 'px';
             div.style.zIndex = '100';
             div.style.zIndex = '100';
             div.style.transform = 'translate(-50%, -50%)';
             div.style.transform = 'translate(-50%, -50%)';
             div.style.cursor = (marker.action && marker.action !== 'none') ? 'pointer' : 'default';
             div.style.cursor = (marker.action && marker.action !== 'none') ? 'pointer' : 'default';
              
              
             var tamanhoIcone = Math.max(20, Math.floor(20 * currentZoom));
             var tamanhoIcone = Math.max(20, Math.floor(20 * zoomAtual));
             var htmlIcone = marker.iconBase64 ?  
             var htmlIcone = marker.iconBase64 ? '<img src="' + marker.iconBase64 + '" style="width:' + tamanhoIcone + 'px; height:' + tamanhoIcone + 'px;">' : '<span style="font-size:' + tamanhoIcone + 'px;">📍</span>';
                '<img src="' + marker.iconBase64 + '" style="width:' + tamanhoIcone + 'px; height:' + tamanhoIcone + 'px;">' :  
                '<span style="font-size:' + tamanhoIcone + 'px;">📍</span>';
              
              
             var textoAcao = '';
             var textoAcao = '';
             switch(marker.action) {
             if (marker.action === 'popup') textoAcao = '📋 Informações';
                case 'popup': textoAcao = '📋 Informações'; break;
            else if (marker.action === 'nextFloor') textoAcao = '⬆️ Próximo andar';
                case 'nextFloor': textoAcao = '⬆️ Próximo andar'; break;
            else if (marker.action === 'prevFloor') textoAcao = '⬇️ Andar anterior';
                case 'prevFloor': textoAcao = '⬇️ Andar anterior'; break;
            else if (marker.action === 'gotoFloor') textoAcao = '🎯 Ir para andar';
                case 'gotoFloor': textoAcao = '🎯 Ir para andar'; break;
            else if (marker.action === 'link') textoAcao = '🔗 Link externo';
                case 'link': textoAcao = '🔗 Link externo'; break;
            else textoAcao = '📍 Clique';
                default: textoAcao = '📍 Clique';
            }
              
              
             div.innerHTML = `
             div.innerHTML = '<div style="display:flex; align-items:center; justify-content:center;">' + htmlIcone + '</div>' +
                <div style="display:flex; align-items:center; justify-content:center;">${htmlIcone}</div>
                 '<div style="position:absolute; bottom:100%; left:50%; transform:translateX(-50%); background:#1e293b; color:white; padding:4px 8px; border-radius:6px; font-size:10px; white-space:nowrap; opacity:0; visibility:hidden; transition:0.2s; pointer-events:none;">' +
                 <div style="position:absolute; bottom:100%; left:50%; transform:translateX(-50%); background:#1e293b; color:white; padding:4px 8px; border-radius:6px; font-size:10px; white-space:nowrap; opacity:0; visibility:hidden; transition:0.2s; pointer-events:none;">
                '<strong>' + (marker.name || 'Marcador') + '</strong><br><small>' + textoAcao + '</small></div>' +
                    <strong>${marker.name || 'Marcador'}</strong><br>
                 '<div style="position:absolute; top:-8px; right:-8px; background:#ef4444; color:white; font-size:9px; min-width:16px; height:16px; border-radius:10px; display:flex; align-items:center; justify-content:center; padding:0 4px; ' + (!marker.hasBadge ? 'display:none;' : '') + '">' + (marker.hasBadge ? (marker.badgeNumber || marker.number || '') : '') + '</div>';
                    <small>${textoAcao}</small>
                </div>
                 <div style="position:absolute; top:-8px; right:-8px; background:#ef4444; color:white; font-size:9px; min-width:16px; height:16px; border-radius:10px; display:flex; align-items:center; justify-content:center; padding:0 4px; ${!marker.hasBadge ? 'display:none;' : ''}">${marker.hasBadge ? (marker.badgeNumber || marker.number || '') : ''}</div>
            `;
              
              
             div.addEventListener('mouseenter', function() {
             div.addEventListener('mouseenter', function() { this.style.transform = 'translate(-50%, -50%) scale(1.15)'; var tp = this.children[1]; if (tp) { tp.style.opacity = '1'; tp.style.visibility = 'visible'; } });
                this.style.transform = 'translate(-50%, -50%) scale(1.15)';
             div.addEventListener('mouseleave', function() { this.style.transform = 'translate(-50%, -50%) scale(1)'; var tp = this.children[1]; if (tp) { tp.style.opacity = '0'; tp.style.visibility = 'hidden'; } });
                var tooltip = this.children[1];
                if (tooltip) {
                    tooltip.style.opacity = '1';
                    tooltip.style.visibility = 'visible';
                }
            });
           
             div.addEventListener('mouseleave', function() {
                this.style.transform = 'translate(-50%, -50%) scale(1)';
                var tooltip = this.children[1];
                if (tooltip) {
                    tooltip.style.opacity = '0';
                    tooltip.style.visibility = 'hidden';
                }
            });
              
              
             if (marker.action && marker.action !== 'none') {
             if (marker.action && marker.action !== 'none') {
                 div.addEventListener('click', function(e) {
                 div.addEventListener('click', function(e) {
                     e.stopPropagation();
                     e.stopPropagation();
                     executarAcao(marker);
                     if (marker.action === 'popup') alert(marker.name + '\n\n' + (marker.actionData?.text || 'Sem informações'));
                    else if (marker.action === 'nextFloor') irProximoAndar();
                    else if (marker.action === 'prevFloor') irAndarAnterior();
                    else if (marker.action === 'gotoFloor' && marker.actionData?.floorId !== undefined) irParaAndar(marker.actionData.floorId);
                    else if (marker.action === 'link' && marker.actionData?.url && marker.actionData.url !== '#') window.open(marker.actionData.url, marker.actionData.target || '_blank');
                 });
                 });
             }
             }
           
             return div;
             return div;
         }
         }
          
          
         function executarAcao(marker) {
         function atualizarPosicaoMarcadores() {
             switch(marker.action) {
             for (var i = 0; i < marcadores.length; i++) {
                 case 'popup':
                 var item = marcadores[i];
                    alert(marker.name + '\n\n' + (marker.actionData?.text || 'Sem informações'));
                if (parseInt(item.el.getAttribute('data-floor')) === andarAtual) {
                     break;
                     item.el.style.left = (item.data.x * zoomAtual) + 'px';
                case 'nextFloor':
                     item.el.style.top = (item.data.y * zoomAtual) + 'px';
                     proximoAndar();
                     var ts = Math.max(20, Math.floor(20 * zoomAtual));
                    break;
                     var idiv = item.el.children[0];
                case 'prevFloor':
                     if (idiv) {
                     andarAnterior();
                         if (item.data.iconBase64) idiv.innerHTML = '<img src="' + item.data.iconBase64 + '" style="width:' + ts + 'px; height:' + ts + 'px;">';
                     break;
                        else idiv.innerHTML = '<span style="font-size:' + ts + 'px;">📍</span>';
                case 'gotoFloor':
                     if (marker.actionData?.floorId !== undefined) {
                         irParaAndar(marker.actionData.floorId);
                     }
                     }
                    break;
                 }
                 case 'link':
                    if (marker.actionData?.url && marker.actionData.url !== '#') {
                        window.open(marker.actionData.url, marker.actionData.target || '_blank');
                    }
                    break;
             }
             }
         }
         }
          
          
         function proximoAndar() {
         function irProximoAndar() {
             var idx = -1;
             var idx = -1;
             for (var i = 0; i < config.layers.length; i++) {
             for (var i = 0; i < config.layers.length; i++) {
                 if (config.layers[i].id === currentFloor) idx = i;
                 if (config.layers[i].id === andarAtual) idx = i;
            }
            if (idx < config.layers.length - 1) {
                irParaAndar(config.layers[idx + 1].id);
             }
             }
            if (idx < config.layers.length - 1) irParaAndar(config.layers[idx + 1].id);
         }
         }
          
          
         function andarAnterior() {
         function irAndarAnterior() {
             var idx = -1;
             var idx = -1;
             for (var i = 0; i < config.layers.length; i++) {
             for (var i = 0; i < config.layers.length; i++) {
                 if (config.layers[i].id === currentFloor) idx = i;
                 if (config.layers[i].id === andarAtual) idx = i;
            }
            if (idx > 0) {
                irParaAndar(config.layers[idx - 1].id);
             }
             }
            if (idx > 0) irParaAndar(config.layers[idx - 1].id);
         }
         }
          
          
         function irParaAndar(floorId) {
         function irParaAndar(floorId) {
             currentFloor = floorId;
             andarAtual = floorId;
             for (var i = 0; i < layers.length; i++) {
             for (var i = 0; i < camadas.length; i++) {
                 var layerFloor = parseInt(layers[i].getAttribute('data-floor'));
                 var lf = parseInt(camadas[i].getAttribute('data-floor'));
                 layers[i].style.display = layerFloor === floorId ? 'block' : 'none';
                 camadas[i].style.display = lf === floorId ? 'block' : 'none';
             }
             }
             var floorData = null;
             var fd = null;
             for (var i = 0; i < config.layers.length; i++) {
             for (var i = 0; i < config.layers.length; i++) {
                 if (config.layers[i].id === floorId) floorData = config.layers[i];
                 if (config.layers[i].id === floorId) fd = config.layers[i];
            }
            if (floorData && floorNameSpan) floorNameSpan.textContent = floorData.name;
            atualizarPosicaoMarkers();
        }
       
        function atualizarPosicaoMarkers() {
            for (var i = 0; i < markers.length; i++) {
                var item = markers[i];
                if (parseInt(item.el.getAttribute('data-floor')) === currentFloor) {
                    item.el.style.left = (item.data.x * currentZoom) + 'px';
                    item.el.style.top = (item.data.y * currentZoom) + 'px';
                    var tamanhoIcone = Math.max(20, Math.floor(20 * currentZoom));
                    var iconDiv = item.el.children[0];
                    if (iconDiv) {
                        if (item.data.iconBase64) {
                            iconDiv.innerHTML = '<img src="' + item.data.iconBase64 + '" style="width:' + tamanhoIcone + 'px; height:' + tamanhoIcone + 'px;">';
                        } else {
                            iconDiv.innerHTML = '<span style="font-size:' + tamanhoIcone + 'px;">📍</span>';
                        }
                    }
                }
             }
             }
            var nomeSpan = document.getElementById('floor-name-' + id);
            if (nomeSpan && fd) nomeSpan.textContent = fd.name;
            atualizarPosicaoMarcadores();
         }
         }
          
          
         function zoomIn() {
         function zoomIn() {
             if (currentZoom < maxZoom) {
             if (zoomAtual < zoomMax) {
                 var oldZoom = currentZoom;
                 var old = zoomAtual;
                 currentZoom = Math.min(currentZoom + zoomStep, maxZoom);
                 zoomAtual = Math.min(zoomAtual + zoomPasso, zoomMax);
                 aplicarZoom(oldZoom);
                 aplicarZoom(old);
             }
             }
         }
         }
          
          
         function zoomOut() {
         function zoomOut() {
             if (currentZoom > minZoom) {
             if (zoomAtual > zoomMin) {
                 var oldZoom = currentZoom;
                 var old = zoomAtual;
                 currentZoom = Math.max(currentZoom - zoomStep, minZoom);
                 zoomAtual = Math.max(zoomAtual - zoomPasso, zoomMin);
                 aplicarZoom(oldZoom);
                 aplicarZoom(old);
             }
             }
         }
         }
          
          
         function aplicarZoom(oldZoom) {
         function aplicarZoom(oldZoom) {
             var images = document.querySelectorAll('.mapa-image');
             var imgs = document.querySelectorAll('.mapa-image');
             for (var i = 0; i < images.length; i++) {
             for (var i = 0; i < imgs.length; i++) imgs[i].style.transform = 'scale(' + zoomAtual + ')';
                images[i].style.transform = 'scale(' + currentZoom + ')';
            }
             var rect = viewport.getBoundingClientRect();
             var rect = viewport.getBoundingClientRect();
             var cx = rect.width / 2, cy = rect.height / 2;
             var cx = rect.width / 2, cy = rect.height / 2;
             var sx = (viewport.scrollLeft + cx) / oldZoom;
             var sx = (viewport.scrollLeft + cx) / oldZoom, sy = (viewport.scrollTop + cy) / oldZoom;
            var sy = (viewport.scrollTop + cy) / oldZoom;
             viewport.scrollLeft = sx * zoomAtual - cx;
             viewport.scrollLeft = sx * currentZoom - cx;
             viewport.scrollTop = sy * zoomAtual - cy;
             viewport.scrollTop = sy * currentZoom - cy;
             atualizarPosicaoMarcadores();
             atualizarPosicaoMarkers();
            var zoomSpan = document.getElementById('zoom-level-' + id);
             if (zoomLevelSpan) zoomLevelSpan.textContent = Math.round(currentZoom * 100) + '%';
             if (zoomSpan) zoomSpan.textContent = Math.round(zoomAtual * 100) + '%';
         }
         }
          
          
         function resetarView() {
         function resetarView() {
             currentZoom = config.mapConfig.defaultZoom || 1;
             zoomAtual = config.mapConfig.defaultZoom || 1;
             var images = document.querySelectorAll('.mapa-image');
             var imgs = document.querySelectorAll('.mapa-image');
             for (var i = 0; i < images.length; i++) {
             for (var i = 0; i < imgs.length; i++) imgs[i].style.transform = 'scale(' + zoomAtual + ')';
                images[i].style.transform = 'scale(' + currentZoom + ')';
            }
             viewport.scrollLeft = 0;
             viewport.scrollLeft = 0;
             viewport.scrollTop = 0;
             viewport.scrollTop = 0;
             atualizarPosicaoMarkers();
             atualizarPosicaoMarcadores();
             if (zoomLevelSpan) zoomLevelSpan.textContent = Math.round(currentZoom * 100) + '%';
            var zoomSpan = document.getElementById('zoom-level-' + id);
             if (zoomSpan) zoomSpan.textContent = Math.round(zoomAtual * 100) + '%';
         }
         }
          
          
         // Eventos dos botões
         // Conectar botões
         var btnZoomIn = toolbar.querySelector('.mapa-zoom-in');
         document.getElementById('zoom-in-' + id).addEventListener('click', zoomIn);
         var btnZoomOut = toolbar.querySelector('.mapa-zoom-out');
         document.getElementById('zoom-out-' + id).addEventListener('click', zoomOut);
         var btnReset = toolbar.querySelector('.mapa-reset');
         document.getElementById('reset-' + id).addEventListener('click', resetarView);
         var btnPrev = navDiv.querySelector('.mapa-prev-floor');
         document.getElementById('prev-' + id).addEventListener('click', irAndarAnterior);
         var btnNext = navDiv.querySelector('.mapa-next-floor');
         document.getElementById('next-' + id).addEventListener('click', irProximoAndar);
          
          
        if (btnZoomIn) btnZoomIn.addEventListener('click', zoomIn);
         // Arrastar
        if (btnZoomOut) btnZoomOut.addEventListener('click', zoomOut);
        if (btnReset) btnReset.addEventListener('click', resetarView);
        if (btnPrev) btnPrev.addEventListener('click', andarAnterior);
        if (btnNext) btnNext.addEventListener('click', proximoAndar);
       
         // Pan
         viewport.addEventListener('mousedown', function(e) {
         viewport.addEventListener('mousedown', function(e) {
             if (e.target.closest('.mapa-marker')) return;
             if (e.target.closest('.mapa-marker')) return;
             isPanning = true;
             arrastando = true;
             panStart = { x: e.clientX, y: e.clientY, scrollLeft: viewport.scrollLeft, scrollTop: viewport.scrollTop };
             arrasteInicio = { x: e.clientX, y: e.clientY, scrollLeft: viewport.scrollLeft, scrollTop: viewport.scrollTop };
             viewport.style.cursor = 'grabbing';
             viewport.style.cursor = 'grabbing';
             e.preventDefault();
             e.preventDefault();
Linha 402: Linha 295:
          
          
         window.addEventListener('mousemove', function(e) {
         window.addEventListener('mousemove', function(e) {
             if (!isPanning) return;
             if (!arrastando) return;
             viewport.scrollLeft = panStart.scrollLeft - (e.clientX - panStart.x);
             viewport.scrollLeft = arrasteInicio.scrollLeft - (e.clientX - arrasteInicio.x);
             viewport.scrollTop = panStart.scrollTop - (e.clientY - panStart.y);
             viewport.scrollTop = arrasteInicio.scrollTop - (e.clientY - arrasteInicio.y);
         });
         });
          
          
         window.addEventListener('mouseup', function() {
         window.addEventListener('mouseup', function() { arrastando = false; viewport.style.cursor = 'grab'; });
            isPanning = false;
            viewport.style.cursor = 'grab';
        });
          
          
        // Zoom com scroll
         viewport.addEventListener('wheel', function(e) {
         viewport.addEventListener('wheel', function(e) {
             if (e.ctrlKey) {
             if (e.ctrlKey) { e.preventDefault(); e.deltaY < 0 ? zoomIn() : zoomOut(); }
                e.preventDefault();
                e.deltaY < 0 ? zoomIn() : zoomOut();
            }
         });
         });
          
          
        // Tracking coordenadas
         viewport.addEventListener('mousemove', function(e) {
         viewport.addEventListener('mousemove', function(e) {
             var rect = viewport.getBoundingClientRect();
             var rect = viewport.getBoundingClientRect();
             var x = (e.clientX - rect.left + viewport.scrollLeft) / currentZoom;
             var x = (e.clientX - rect.left + viewport.scrollLeft) / zoomAtual;
             var y = (e.clientY - rect.top + viewport.scrollTop) / currentZoom;
             var y = (e.clientY - rect.top + viewport.scrollTop) / zoomAtual;
             coordsDiv.textContent = '📍 ' + Math.round(x) + ', ' + Math.round(y) + ' | ' + Math.round(currentZoom * 100) + '%';
             coordsDiv.textContent = '📍 ' + Math.round(x) + ', ' + Math.round(y) + ' | ' + Math.round(zoomAtual * 100) + '%';
         });
         });
          
          

Edição atual tal como às 20h38min de 9 de abril de 2026