Mudanças entre as edições de "Widget:MapaViewer"

De Wiki Gla
Ir para navegação Ir para pesquisar
 
(7 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
<includeonly><div id="mapa-viewer-{{{id|mapa-default}}}" style="width:{{{largura|100%}}}; height:{{{altura|500px}}};">Atirei</div>
<includeonly>
<div id="mapa-viewer-<!--{$id|escape:'quotes'|default:'mapa-default'}-->" style="width: <!--{$largura|escape:'quotes'|default:'100%'}-->; height: <!--{$altura|escape:'quotes'|default:'500px'}-->;"></div>


<script>
<script>
(function() {
class MapaViewer {
    // Classe MapaViewer integrada
    class MapaViewer {
         constructor(containerId, configOrUrl) {
         constructor(containerId, configOrUrl) {
             this.container = document.getElementById(containerId);
             this.container = document.getElementById(containerId);
Linha 27: Linha 26:


             if (typeof configOrUrl === 'string') {
             if (typeof configOrUrl === 'string') {
                 if (configOrUrl.startsWith('http') || configOrUrl.endsWith('.json')) {
                 if (configOrUrl.startsWith('http')) {
                     this.loadJSON(configOrUrl);
                     this.loadJSON(configOrUrl);
                 } else {
                 } else {
Linha 48: Linha 47:


             this.viewport = document.createElement('div');
             this.viewport = document.createElement('div');
             this.viewport.style.cssText = `
             this.viewport.style.cssText = 'width:100%;height:100%;overflow:hidden;cursor:grab;position:relative;';
                width: 100%;
                height: 100%;
                overflow: hidden;
                cursor: grab;
                position: relative;
            `;


             this.layersContainer = document.createElement('div');
             this.layersContainer = document.createElement('div');
             this.layersContainer.style.cssText = `
             this.layersContainer.style.cssText = 'position:relative;min-width:100%;min-height:100%;';
                position: relative;
                min-width: 100%;
                min-height: 100%;
            `;


             this.viewport.appendChild(this.layersContainer);
             this.viewport.appendChild(this.layersContainer);
Linha 175: Linha 164:
             this.layers = [];
             this.layers = [];
             this.markers = [];
             this.markers = [];
           
             this.config.layers.forEach((layer) => {
             this.config.layers.forEach((layer) => {
                 const $layer = document.createElement('div');
                 const $layer = document.createElement('div');
Linha 185: Linha 175:
                 $layer.style.transform = `translate(${layer.offsetX}px, ${layer.offsetY}px)`;
                 $layer.style.transform = `translate(${layer.offsetX}px, ${layer.offsetY}px)`;
                 $layer.style.opacity = (layer.opacity || 100) / 100;
                 $layer.style.opacity = (layer.opacity || 100) / 100;
               
                 const $img = document.createElement('img');
                 const $img = document.createElement('img');
                 $img.className = 'mapa-image';
                 $img.className = 'mapa-image';
Linha 192: Linha 183:
                 $img.style.pointerEvents = 'none';
                 $img.style.pointerEvents = 'none';
                 $img.src = layer.imageUrl || '';
                 $img.src = layer.imageUrl || '';
               
                 $img.onerror = () => {
                 $img.onerror = () => {
                     const canvas = document.createElement('canvas');
                     const canvas = document.createElement('canvas');
Linha 204: Linha 196:
                     $img.src = canvas.toDataURL();
                     $img.src = canvas.toDataURL();
                 };
                 };
               
                 $img.onload = () => {
                 $img.onload = () => {
                     this.layersContainer.style.width = `${$img.width}px`;
                     this.layersContainer.style.width = `${$img.width}px`;
Linha 209: Linha 202:
                     this.centerMap();
                     this.centerMap();
                 };
                 };
               
                 $layer.appendChild($img);
                 $layer.appendChild($img);
               
                 const $markersContainer = document.createElement('div');
                 const $markersContainer = document.createElement('div');
                 $markersContainer.style.position = 'absolute';
                 $markersContainer.style.position = 'absolute';
Linha 217: Linha 212:
                 $markersContainer.style.height = '100%';
                 $markersContainer.style.height = '100%';
                 $markersContainer.style.pointerEvents = 'none';
                 $markersContainer.style.pointerEvents = 'none';
               
                 if (layer.markers && layer.markers.length) {
                 if (layer.markers && layer.markers.length) {
                     layer.markers.forEach(marker => {
                     layer.markers.forEach(marker => {
Linha 224: Linha 220:
                     });
                     });
                 }
                 }
               
                 $layer.appendChild($markersContainer);
                 $layer.appendChild($markersContainer);
                 this.layersContainer.appendChild($layer);
                 this.layersContainer.appendChild($layer);
Linha 237: Linha 234:
             $marker.setAttribute('data-marker-id', marker.id);
             $marker.setAttribute('data-marker-id', marker.id);
             $marker.setAttribute('data-floor', floorId);
             $marker.setAttribute('data-floor', floorId);
            $marker.setAttribute('data-x', marker.x);
            $marker.setAttribute('data-y', marker.y);
             $marker.style.position = 'absolute';
             $marker.style.position = 'absolute';
             $marker.style.left = `${marker.x * this.currentZoom}px`;
             $marker.style.left = `${marker.x * this.currentZoom}px`;
Linha 245: Linha 240:
             $marker.style.transform = 'translate(-50%, -50%)';
             $marker.style.transform = 'translate(-50%, -50%)';
             $marker.style.pointerEvents = 'auto';
             $marker.style.pointerEvents = 'auto';
           
             const hasAction = marker.action && marker.action !== 'none';
             const hasAction = marker.action && marker.action !== 'none';
             $marker.style.cursor = hasAction ? 'pointer' : 'default';
             $marker.style.cursor = hasAction ? 'pointer' : 'default';
           
             const iconSize = Math.max(16, Math.floor(16 * this.currentZoom));
             const iconSize = Math.max(16, Math.floor(16 * this.currentZoom));
             let iconHtml = '';
             let iconHtml = '';
           
             if (marker.iconUrl && marker.iconUrl !== '') {
             if (marker.iconUrl && marker.iconUrl !== '') {
                 iconHtml = `<img src="${marker.iconUrl}" style="width:${iconSize}px; height:${iconSize}px; object-fit:contain;">`;
                 iconHtml = `<img src="${marker.iconUrl}" style="width:${iconSize}px; height:${iconSize}px; object-fit:contain;">`;
Linha 254: Linha 252:
                 iconHtml = `<i class="fas fa-map-marker-alt" style="font-size:${iconSize}px;"></i>`;
                 iconHtml = `<i class="fas fa-map-marker-alt" style="font-size:${iconSize}px;"></i>`;
             }
             }
           
             $marker.innerHTML = `
             $marker.innerHTML = `
                 <div class="mapa-marker-icon" style="display:flex; align-items:center; justify-content:center; pointer-events:none;">${iconHtml}</div>
                 <div class="mapa-marker-icon" style="display:flex; align-items:center; justify-content:center; pointer-events:none;">${iconHtml}</div>
                 <div class="mapa-marker-tooltip" 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; z-index:101;">
                 <div class="mapa-marker-tooltip" 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; z-index:101;">
                     <strong>${marker.name || 'Marcador'}</strong><br>
                     <strong>${marker.name || 'Marcador'}</strong>
                    <small>${this.getActionText(marker.action)}</small>
                 </div>
                 </div>
                <div class="mapa-marker-badge" 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>
             `;
             `;
           
             $marker.addEventListener('mouseenter', () => {
             $marker.addEventListener('mouseenter', () => {
                 $marker.style.transform = 'translate(-50%, -50%) scale(1.1)';
                 $marker.style.transform = 'translate(-50%, -50%) scale(1.1)';
Linha 270: Linha 268:
                 }
                 }
             });
             });
           
             $marker.addEventListener('mouseleave', () => {
             $marker.addEventListener('mouseleave', () => {
                 $marker.style.transform = 'translate(-50%, -50%) scale(1)';
                 $marker.style.transform = 'translate(-50%, -50%) scale(1)';
Linha 278: Linha 277:
                 }
                 }
             });
             });
           
             if (hasAction) {
             if (hasAction) {
                 $marker.addEventListener('click', (e) => {
                 $marker.addEventListener('click', (e) => {
Linha 284: Linha 284:
                 });
                 });
             }
             }
           
             return $marker;
             return $marker;
        }
        getActionText(action) {
            const actions = {
                'popup': '📋 Informações',
                'nextFloor': '⬆️ Próximo andar',
                'prevFloor': '⬇️ Andar anterior',
                'gotoFloor': '🎯 Ir para andar',
                'link': '🔗 Link externo',
                'none': '🔇 Visual'
            };
            return actions[action] || '📍 Clique';
         }
         }


Linha 343: Linha 332:
                 });
                 });
             }
             }
           
             const content = modal.querySelector('#mapa-viewer-popup-content');
             const content = modal.querySelector('#mapa-viewer-popup-content');
            const iconHtml = (marker.iconUrl && marker.iconUrl !== '') ?
                `<img src="${marker.iconUrl}" style="width:32px; height:32px; display:inline-block; vertical-align:middle; margin-right:8px;">` :
                `<i class="fas fa-map-marker-alt" style="font-size:24px; margin-right:8px;"></i>`;
             content.innerHTML = `
             content.innerHTML = `
                ${marker.actionData?.image ? `<img src="${marker.actionData.image}" style="max-width:100%; border-radius:12px; margin-bottom:16px;">` : ''}
                 <div style="display:flex; align-items:center; margin-bottom:12px;">
                 <div style="display:flex; align-items:center; margin-bottom:12px;">
                    ${iconHtml}
                     <h3 style="margin:0; color:#a5b4fc;">${marker.name}</h3>
                     <h3 style="margin:0; color:#a5b4fc;">${marker.name}</h3>
                 </div>
                 </div>
Linha 365: Linha 350:
                     element.style.left = `${zoomedX}px`;
                     element.style.left = `${zoomedX}px`;
                     element.style.top = `${zoomedY}px`;
                     element.style.top = `${zoomedY}px`;
                   
                     const iconSize = Math.max(24, Math.floor(24 * this.currentZoom));
                     const iconSize = Math.max(24, Math.floor(24 * this.currentZoom));
                     const iconDiv = element.querySelector('.mapa-marker-icon');
                     const iconDiv = element.querySelector('.mapa-marker-icon');
Linha 373: Linha 359:
                                 img.style.width = `${iconSize}px`;
                                 img.style.width = `${iconSize}px`;
                                 img.style.height = `${iconSize}px`;
                                 img.style.height = `${iconSize}px`;
                            } else {
                                iconDiv.innerHTML = `<img src="${data.iconUrl}" style="width:${iconSize}px; height:${iconSize}px; object-fit:contain;">`;
                             }
                             }
                         } else {
                         } else {
Linha 380: Linha 364:
                             if (icon) {
                             if (icon) {
                                 icon.style.fontSize = `${iconSize}px`;
                                 icon.style.fontSize = `${iconSize}px`;
                            } else {
                                iconDiv.innerHTML = `<i class="fas fa-map-marker-alt" style="font-size:${iconSize}px;"></i>`;
                             }
                             }
                         }
                         }
Linha 424: Linha 406:
             const scrollX = (this.viewport.scrollLeft + centerX) / oldZoom;
             const scrollX = (this.viewport.scrollLeft + centerX) / oldZoom;
             const scrollY = (this.viewport.scrollTop + centerY) / oldZoom;
             const scrollY = (this.viewport.scrollTop + centerY) / oldZoom;
           
             document.querySelectorAll('.mapa-image').forEach(img => {
             document.querySelectorAll('.mapa-image').forEach(img => {
                 img.style.transform = `scale(${this.currentZoom})`;
                 img.style.transform = `scale(${this.currentZoom})`;
             });
             });
           
             this.updateMarkersPosition();
             this.updateMarkersPosition();
             this.viewport.scrollLeft = scrollX * this.currentZoom - centerX;
             this.viewport.scrollLeft = scrollX * this.currentZoom - centerX;
Linha 442: Linha 426:


         prevFloor() {
         prevFloor() {
             const idx = this.config.layers.findIndex(l => l.id === this.currentFloor);
             const layers = this.config.layers.sort((a,b) => a.id - b.id);
             if (idx < this.config.layers.length - 1) {
            const currentIndex = layers.findIndex(l => l.id === this.currentFloor);
                 this.goToFloor(this.config.layers[idx + 1].id);
             if (currentIndex > 0) {
                 this.goToFloor(layers[currentIndex - 1].id);
             }
             }
         }
         }


         nextFloor() {
         nextFloor() {
             const idx = this.config.layers.findIndex(l => l.id === this.currentFloor);
             const layers = this.config.layers.sort((a,b) => a.id - b.id);
             if (idx > 0) {
            const currentIndex = layers.findIndex(l => l.id === this.currentFloor);
                 this.goToFloor(this.config.layers[idx - 1].id);
             if (currentIndex < layers.length - 1) {
                 this.goToFloor(layers[currentIndex + 1].id);
             }
             }
         }
         }
Linha 481: Linha 467:
                 }
                 }
             }
             }
           
             const viewportWidth = this.viewport.clientWidth;
             const viewportWidth = this.viewport.clientWidth;
             const viewportHeight = this.viewport.clientHeight;
             const viewportHeight = this.viewport.clientHeight;
             let screenX = (x + (this.currentFloor?.offsetX || 0)) * this.currentZoom;
             let screenX = x * this.currentZoom;
             let screenY = (y + (this.currentFloor?.offsetY || 0)) * this.currentZoom;
             let screenY = y * this.currentZoom;
             let targetScrollX = screenX - (viewportWidth / 2);
             let targetScrollX = screenX - (viewportWidth / 2);
             let targetScrollY = screenY - (viewportHeight / 2);
             let targetScrollY = screenY - (viewportHeight / 2);
           
             this.viewport.scrollLeft = Math.max(0, targetScrollX);
             this.viewport.scrollLeft = Math.max(0, targetScrollX);
             this.viewport.scrollTop = Math.max(0, targetScrollY);
             this.viewport.scrollTop = Math.max(0, targetScrollY);
            this.showTemporaryHighlight(x, y);
        this.showTemporaryHighlight(x, y);
         }
    }
 
    showTemporaryHighlight(x, y) {
        const highlight = document.createElement('div');
         highlight.style.cssText = `
            position: absolute;
            width: 60px;
            height: 60px;
            left: ${(x + (this.currentFloor?.offsetX || 0)) * this.currentZoom}px;
            top: ${(y + (this.currentFloor?.offsetY || 0)) * this.currentZoom}px;
            transform: translate(-50%, -50%);
            background: radial-gradient(circle, rgba(255,215,0,0.8) 0%, rgba(255,215,0,0) 70%);
            border-radius: 50%;
            pointer-events: none;
            z-index: 200;
            animation: mapa-highlight-pulse 0.6s ease-out 3;
        `;


         showTemporaryHighlight(x, y) {
         this.layersContainer.appendChild(highlight);
            const highlight = document.createElement('div');
            highlight.style.cssText = `
                position: absolute;
                width: 60px;
                height: 60px;
                left: ${(x + (this.currentFloor?.offsetX || 0)) * this.currentZoom}px;
                top: ${(y + (this.currentFloor?.offsetY || 0)) * this.currentZoom}px;
                transform: translate(-50%, -50%);
                background: radial-gradient(circle, rgba(255,215,0,0.8) 0%, rgba(255,215,0,0) 70%);
                border-radius: 50%;
                pointer-events: none;
                z-index: 200;
                animation: mapa-highlight-pulse 0.6s ease-out 3;
            `;
            this.layersContainer.appendChild(highlight);
            setTimeout(() => {
                highlight.remove();
            }, 1800);
        }


        setTimeout(() => {
            highlight.remove();
        }, 1800);
    }
         setupKeyboardShortcuts() {
         setupKeyboardShortcuts() {
             const handler = (e) => {
             const handler = (e) => {
Linha 544: Linha 533:
     }
     }


     const style = document.createElement('style');
(function() {
     style.textContent = `
     var containerId = 'mapa-viewer-<!--{$id|escape:'quotes'|default:'mapa-default'}-->';
        @keyframes mapa-highlight-pulse {
     var configJSON = <!--{$configJSON|default:'null'}-->;  // Recebe o JSON
            0% { transform: translate(-50%, -50%) scale(0.5); opacity: 1; }
    var urlJSON = '<!--{$url|escape:'quotes'}-->';        // Recebe URL do JSON
             100% { transform: translate(-50%, -50%) scale(2); opacity: 0; }
   
    // Nome da variável global única para este mapa
    var globalVarName = 'mapaViewer_<!--{$id|escape:'quotes'|default:'mapa-default'}-->';
   
    function iniciarMapa() {
        var container = document.getElementById(containerId);
        if (!container) return;
        if (container.hasAttribute('data-iniciado')) return;
       
        container.setAttribute('data-iniciado', 'true');
       
        var viewer = null;
       
        if (configJSON && configJSON !== 'null') {
            // Configuração inline
            viewer = new MapaViewer(containerId, configJSON);
        } else if (urlJSON) {
            // Configuração via URL
             fetch(urlJSON)
                .then(response => response.json())
                .then(config => {
                    window[globalVarName] = new MapaViewer(containerId, config);
                })
                .catch(error => {
                    console.error('Erro ao carregar mapa:', error);
                    container.innerHTML = '<div style="color:red;padding:20px;">❌ Erro ao carregar mapa: ' + error.message + '</div>';
                });
            return; // Sai porque o fetch é assíncrono
        } else {
            container.innerHTML = '<div style="color:red;padding:20px;">❌ Nenhuma configuração fornecida</div>';
            return;
         }
         }
    `;
       
    document.head.appendChild(style);
        // Guarda a instância globalmente para acesso externo
 
        if (viewer) {
    window.MapaViewer = MapaViewer;
            window[globalVarName] = viewer;
 
            console.log('Mapa inicializado! Use window.' + globalVarName + ' para controlar');
    const containerId = 'mapa-viewer-{{{id|mapa-default}}}';
             console.log('Exemplo: window.' + globalVarName + '.nextFloor()');
   
    // Aguardar DOM carregar
    function initMapaViewer() {
        const container = document.getElementById(containerId);
        if (container && !container.hasAttribute('data-initialized')) {
             container.setAttribute('data-initialized', 'true');
           
            // Configuração do mapa
            {{{configuracao}}}
         }
         }
     }
     }
      
      
     if (document.readyState === 'loading') {
     if (document.readyState === 'loading') {
         document.addEventListener('DOMContentLoaded', initMapaViewer);
         document.addEventListener('DOMContentLoaded', iniciarMapa);
     } else {
     } else {
         initMapaViewer();
         iniciarMapa();
     }
     }
})();
})();
</script></includeonly>
</script>
</includeonly>

Edição atual tal como às 17h15min de 11 de abril de 2026