<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pt-BR">
	<id>https://wiki.gla.com.br/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ceu+azul</id>
	<title>Wiki Gla - Contribuições do usuário [pt-br]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.gla.com.br/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ceu+azul"/>
	<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php/Especial:Contribui%C3%A7%C3%B5es/Ceu_azul"/>
	<updated>2026-05-21T00:39:11Z</updated>
	<subtitle>Contribuições do usuário</subtitle>
	<generator>MediaWiki 1.36.1</generator>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Ceu_azul&amp;diff=46095</id>
		<title>Usuário:Ceu azul</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Ceu_azul&amp;diff=46095"/>
		<updated>2026-04-14T09:35:39Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{MapaViewer&lt;br /&gt;
|id=meu_mapa&lt;br /&gt;
|largura=800px&lt;br /&gt;
|altura=600px&lt;br /&gt;
|url=https://wiki.gla.com.br/index.php/Mapa:BlackMarket?action=raw&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;!--|configJSON={{#invoke:MapaJson|getDados|Mapa:BlackMarket}}--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Foosha1.png&amp;diff=46062</id>
		<title>Arquivo:Foosha1.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Foosha1.png&amp;diff=46062"/>
		<updated>2026-04-11T22:46:52Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Ceu azul carregada uma nova versão de Arquivo:Foosha1.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Foosha0.png&amp;diff=46061</id>
		<title>Arquivo:Foosha0.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Foosha0.png&amp;diff=46061"/>
		<updated>2026-04-11T22:46:51Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Ceu azul carregada uma nova versão de Arquivo:Foosha0.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Foosha4.png&amp;diff=46060</id>
		<title>Arquivo:Foosha4.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Foosha4.png&amp;diff=46060"/>
		<updated>2026-04-11T22:46:50Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Foosha3.png&amp;diff=46059</id>
		<title>Arquivo:Foosha3.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Foosha3.png&amp;diff=46059"/>
		<updated>2026-04-11T22:46:47Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Foosha2.png&amp;diff=46058</id>
		<title>Arquivo:Foosha2.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Foosha2.png&amp;diff=46058"/>
		<updated>2026-04-11T22:46:44Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Foosha1.png&amp;diff=46057</id>
		<title>Arquivo:Foosha1.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Foosha1.png&amp;diff=46057"/>
		<updated>2026-04-11T22:37:41Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Foosha0.png&amp;diff=46056</id>
		<title>Arquivo:Foosha0.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Foosha0.png&amp;diff=46056"/>
		<updated>2026-04-11T22:37:39Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=46026</id>
		<title>Widget:MapaViewer</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=46026"/>
		<updated>2026-04-11T17:15:22Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&amp;quot; style=&amp;quot;width: &amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;; height: &amp;lt;!--{$altura|escape:'quotes'|default:'500px'}--&amp;gt;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
class MapaViewer {&lt;br /&gt;
        constructor(containerId, configOrUrl) {&lt;br /&gt;
            this.container = document.getElementById(containerId);&lt;br /&gt;
            if (!this.container) {&lt;br /&gt;
                console.error(`Container &amp;quot;${containerId}&amp;quot; não encontrado`);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            this.config = null;&lt;br /&gt;
            this.currentZoom = 1;&lt;br /&gt;
            this.currentFloor = 0;&lt;br /&gt;
            this.minZoom = 0.3;&lt;br /&gt;
            this.maxZoom = 3;&lt;br /&gt;
            this.zoomStep = 0.1;&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            this.isPanning = false;&lt;br /&gt;
            this.panStart = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 };&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
&lt;br /&gt;
            this.init();&lt;br /&gt;
&lt;br /&gt;
            if (typeof configOrUrl === 'string') {&lt;br /&gt;
                if (configOrUrl.startsWith('http')) {&lt;br /&gt;
                    this.loadJSON(configOrUrl);&lt;br /&gt;
                } else {&lt;br /&gt;
                    try {&lt;br /&gt;
                        this.loadConfig(JSON.parse(configOrUrl));&lt;br /&gt;
                    } catch(e) {&lt;br /&gt;
                        console.error('JSON inválido:', e);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } else if (configOrUrl) {&lt;br /&gt;
                this.loadConfig(configOrUrl);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        init() {&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.container.style.position = 'relative';&lt;br /&gt;
            this.container.style.overflow = 'hidden';&lt;br /&gt;
            this.container.style.background = '#0f172a';&lt;br /&gt;
&lt;br /&gt;
            this.viewport = document.createElement('div');&lt;br /&gt;
            this.viewport.style.cssText = 'width:100%;height:100%;overflow:hidden;cursor:grab;position:relative;';&lt;br /&gt;
&lt;br /&gt;
            this.layersContainer = document.createElement('div');&lt;br /&gt;
            this.layersContainer.style.cssText = 'position:relative;min-width:100%;min-height:100%;';&lt;br /&gt;
&lt;br /&gt;
            this.viewport.appendChild(this.layersContainer);&lt;br /&gt;
            this.container.appendChild(this.viewport);&lt;br /&gt;
            this.bindEvents();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        bindEvents() {&lt;br /&gt;
            this.viewport.addEventListener('mousedown', (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.closest('.mapa-marker')) return;&lt;br /&gt;
                this.isPanning = true;&lt;br /&gt;
                this.panStart = {&lt;br /&gt;
                    x: e.clientX,&lt;br /&gt;
                    y: e.clientY,&lt;br /&gt;
                    scrollLeft: this.viewport.scrollLeft,&lt;br /&gt;
                    scrollTop: this.viewport.scrollTop&lt;br /&gt;
                };&lt;br /&gt;
                this.viewport.style.cursor = 'grabbing';&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mousemove', (e) =&amp;gt; {&lt;br /&gt;
                if (!this.isPanning) return;&lt;br /&gt;
                this.viewport.scrollLeft = this.panStart.scrollLeft - (e.clientX - this.panStart.x);&lt;br /&gt;
                this.viewport.scrollTop = this.panStart.scrollTop - (e.clientY - this.panStart.y);&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mouseup', () =&amp;gt; {&lt;br /&gt;
                this.isPanning = false;&lt;br /&gt;
                this.viewport.style.cursor = 'grab';&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            this.viewport.addEventListener('wheel', (e) =&amp;gt; {&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                if (e.deltaY &amp;lt; 0) {&lt;br /&gt;
                    this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
                } else {&lt;br /&gt;
                    this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
                }&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
                    const mouseX = e.clientX - rect.left;&lt;br /&gt;
                    const mouseY = e.clientY - rect.top;&lt;br /&gt;
                    const mapX = (this.viewport.scrollLeft + mouseX) / oldZoom;&lt;br /&gt;
                    const mapY = (this.viewport.scrollTop + mouseY) / oldZoom;&lt;br /&gt;
                    &lt;br /&gt;
                    document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                        img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                    });&lt;br /&gt;
                    &lt;br /&gt;
                    this.updateMarkersPosition();&lt;br /&gt;
                    this.viewport.scrollLeft = mapX * this.currentZoom - mouseX;&lt;br /&gt;
                    this.viewport.scrollTop = mapY * this.currentZoom - mouseY;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadConfig(config) {&lt;br /&gt;
            this.config = this.normalizeConfig(config);&lt;br /&gt;
            if (!this.config.layers || this.config.layers.length === 0) {&lt;br /&gt;
                console.error('Nenhuma camada encontrada');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            this.currentFloor = this.config.mapConfig?.initialFloor || 0;&lt;br /&gt;
            this.minZoom = this.config.mapConfig?.minZoom || 0.3;&lt;br /&gt;
            this.maxZoom = this.config.mapConfig?.maxZoom || 3;&lt;br /&gt;
            this.zoomStep = this.config.mapConfig?.zoomStep || 0.1;&lt;br /&gt;
            this.render();&lt;br /&gt;
            this.setupKeyboardShortcuts();&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadJSON(url) {&lt;br /&gt;
            fetch(url)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; this.loadConfig(config))&lt;br /&gt;
                .catch(error =&amp;gt; console.error('Erro ao carregar JSON:', error));&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        normalizeConfig(config) {&lt;br /&gt;
            if (!config.mapConfig) config.mapConfig = {};&lt;br /&gt;
            if (!config.layers) config.layers = [];&lt;br /&gt;
            config.layers.forEach((layer, idx) =&amp;gt; {&lt;br /&gt;
                if (layer.id === undefined) layer.id = idx;&lt;br /&gt;
                if (layer.name === undefined) layer.name = `Andar ${idx + 1}`;&lt;br /&gt;
                if (layer.imageUrl === undefined) layer.imageUrl = '';&lt;br /&gt;
                if (layer.offsetX === undefined) layer.offsetX = 0;&lt;br /&gt;
                if (layer.offsetY === undefined) layer.offsetY = 0;&lt;br /&gt;
                if (layer.scale === undefined || layer.scale === null) layer.scale = 1;&lt;br /&gt;
                if (layer.opacity === undefined || layer.opacity === null) layer.opacity = 100;&lt;br /&gt;
                if (!layer.markers) layer.markers = [];&lt;br /&gt;
                layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                    if (marker.x === undefined) marker.x = 0;&lt;br /&gt;
                    if (marker.y === undefined) marker.y = 0;&lt;br /&gt;
                    if (marker.hasBadge === undefined) marker.hasBadge = false;&lt;br /&gt;
                    if (marker.action === undefined) marker.action = 'popup';&lt;br /&gt;
                    if (!marker.actionData) marker.actionData = {};&lt;br /&gt;
                    if (marker.iconUrl === undefined) marker.iconUrl = '';&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            return config;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        render() {&lt;br /&gt;
            if (!this.config || !this.config.layers.length) {&lt;br /&gt;
                this.showError('Nenhum dado para renderizar');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.layersContainer.innerHTML = '';&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            &lt;br /&gt;
            this.config.layers.forEach((layer) =&amp;gt; {&lt;br /&gt;
                const $layer = document.createElement('div');&lt;br /&gt;
                $layer.className = 'mapa-layer';&lt;br /&gt;
                $layer.setAttribute('data-floor', layer.id);&lt;br /&gt;
                $layer.style.display = layer.id === this.currentFloor ? 'block' : 'none';&lt;br /&gt;
                $layer.style.position = 'absolute';&lt;br /&gt;
                $layer.style.top = '0';&lt;br /&gt;
                $layer.style.left = '0';&lt;br /&gt;
                $layer.style.transform = `translate(${layer.offsetX}px, ${layer.offsetY}px)`;&lt;br /&gt;
                $layer.style.opacity = (layer.opacity || 100) / 100;&lt;br /&gt;
                &lt;br /&gt;
                const $img = document.createElement('img');&lt;br /&gt;
                $img.className = 'mapa-image';&lt;br /&gt;
                $img.style.display = 'block';&lt;br /&gt;
                $img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                $img.style.transformOrigin = '0 0';&lt;br /&gt;
                $img.style.pointerEvents = 'none';&lt;br /&gt;
                $img.src = layer.imageUrl || '';&lt;br /&gt;
                &lt;br /&gt;
                $img.onerror = () =&amp;gt; {&lt;br /&gt;
                    const canvas = document.createElement('canvas');&lt;br /&gt;
                    canvas.width = 800;&lt;br /&gt;
                    canvas.height = 600;&lt;br /&gt;
                    const ctx = canvas.getContext('2d');&lt;br /&gt;
                    ctx.fillStyle = '#334155';&lt;br /&gt;
                    ctx.fillRect(0, 0, canvas.width, canvas.height);&lt;br /&gt;
                    ctx.fillStyle = 'white';&lt;br /&gt;
                    ctx.font = '20px Arial';&lt;br /&gt;
                    ctx.fillText(layer.name || 'Andar', 50, 100);&lt;br /&gt;
                    $img.src = canvas.toDataURL();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $img.onload = () =&amp;gt; {&lt;br /&gt;
                    this.layersContainer.style.width = `${$img.width}px`;&lt;br /&gt;
                    this.layersContainer.style.height = `${$img.height}px`;&lt;br /&gt;
                    this.centerMap();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($img);&lt;br /&gt;
                &lt;br /&gt;
                const $markersContainer = document.createElement('div');&lt;br /&gt;
                $markersContainer.style.position = 'absolute';&lt;br /&gt;
                $markersContainer.style.top = '0';&lt;br /&gt;
                $markersContainer.style.left = '0';&lt;br /&gt;
                $markersContainer.style.width = '100%';&lt;br /&gt;
                $markersContainer.style.height = '100%';&lt;br /&gt;
                $markersContainer.style.pointerEvents = 'none';&lt;br /&gt;
                &lt;br /&gt;
                if (layer.markers &amp;amp;&amp;amp; layer.markers.length) {&lt;br /&gt;
                    layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                        const $marker = this.createMarker(marker, layer.id);&lt;br /&gt;
                        $markersContainer.appendChild($marker);&lt;br /&gt;
                        this.markers.push({ element: $marker, data: marker });&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($markersContainer);&lt;br /&gt;
                this.layersContainer.appendChild($layer);&lt;br /&gt;
                this.layers.push($layer);&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.isReady = true;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        createMarker(marker, floorId) {&lt;br /&gt;
            const $marker = document.createElement('div');&lt;br /&gt;
            $marker.className = 'mapa-marker';&lt;br /&gt;
            $marker.setAttribute('data-marker-id', marker.id);&lt;br /&gt;
            $marker.setAttribute('data-floor', floorId);&lt;br /&gt;
            $marker.style.position = 'absolute';&lt;br /&gt;
            $marker.style.left = `${marker.x * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.top = `${marker.y * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.zIndex = '100';&lt;br /&gt;
            $marker.style.transform = 'translate(-50%, -50%)';&lt;br /&gt;
            $marker.style.pointerEvents = 'auto';&lt;br /&gt;
            &lt;br /&gt;
            const hasAction = marker.action &amp;amp;&amp;amp; marker.action !== 'none';&lt;br /&gt;
            $marker.style.cursor = hasAction ? 'pointer' : 'default';&lt;br /&gt;
            &lt;br /&gt;
            const iconSize = Math.max(16, Math.floor(16 * this.currentZoom));&lt;br /&gt;
            let iconHtml = '';&lt;br /&gt;
            &lt;br /&gt;
            if (marker.iconUrl &amp;amp;&amp;amp; marker.iconUrl !== '') {&lt;br /&gt;
                iconHtml = `&amp;lt;img src=&amp;quot;${marker.iconUrl}&amp;quot; style=&amp;quot;width:${iconSize}px; height:${iconSize}px; object-fit:contain;&amp;quot;&amp;gt;`;&lt;br /&gt;
            } else {&lt;br /&gt;
                iconHtml = `&amp;lt;i class=&amp;quot;fas fa-map-marker-alt&amp;quot; style=&amp;quot;font-size:${iconSize}px;&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;`;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            $marker.innerHTML = `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-icon&amp;quot; style=&amp;quot;display:flex; align-items:center; justify-content:center; pointer-events:none;&amp;quot;&amp;gt;${iconHtml}&amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-tooltip&amp;quot; style=&amp;quot;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;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;strong&amp;gt;${marker.name || 'Marcador'}&amp;lt;/strong&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseenter', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1.1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '1';&lt;br /&gt;
                    tooltip.style.visibility = 'visible';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseleave', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '0';&lt;br /&gt;
                    tooltip.style.visibility = 'hidden';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            if (hasAction) {&lt;br /&gt;
                $marker.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    e.stopPropagation();&lt;br /&gt;
                    this.executeAction(marker);&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            return $marker;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        executeAction(marker) {&lt;br /&gt;
            switch (marker.action) {&lt;br /&gt;
                case 'popup':&lt;br /&gt;
                    this.showPopup(marker);&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'nextFloor':&lt;br /&gt;
                    this.nextFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'prevFloor':&lt;br /&gt;
                    this.prevFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'gotoFloor':&lt;br /&gt;
                    if (marker.actionData?.floorId !== undefined) {&lt;br /&gt;
                        this.goToFloor(marker.actionData.floorId);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'link':&lt;br /&gt;
                    if (marker.actionData?.url) {&lt;br /&gt;
                        window.open(marker.actionData.url, marker.actionData.target || '_blank');&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showPopup(marker) {&lt;br /&gt;
            let modal = document.getElementById('mapa-viewer-modal');&lt;br /&gt;
            if (!modal) {&lt;br /&gt;
                modal = document.createElement('div');&lt;br /&gt;
                modal.id = 'mapa-viewer-modal';&lt;br /&gt;
                modal.style.cssText = 'display:none; position:fixed; z-index:10000; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.7); justify-content:center; align-items:center;';&lt;br /&gt;
                modal.innerHTML = `&lt;br /&gt;
                    &amp;lt;div style=&amp;quot;background:#1e293b; border-radius:16px; max-width:350px; width:90%; padding:20px; position:relative; border:1px solid #334155;&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;span style=&amp;quot;position:absolute; right:15px; top:10px; font-size:24px; cursor:pointer; color:#94a3b8;&amp;quot;&amp;gt;&amp;amp;times;&amp;lt;/span&amp;gt;&lt;br /&gt;
                        &amp;lt;div id=&amp;quot;mapa-viewer-popup-content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                `;&lt;br /&gt;
                document.body.appendChild(modal);&lt;br /&gt;
                modal.querySelector('span').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
                    modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
                modal.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    if (e.target === modal) modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const content = modal.querySelector('#mapa-viewer-popup-content');&lt;br /&gt;
            content.innerHTML = `&lt;br /&gt;
                &amp;lt;div style=&amp;quot;display:flex; align-items:center; margin-bottom:12px;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;h3 style=&amp;quot;margin:0; color:#a5b4fc;&amp;quot;&amp;gt;${marker.name}&amp;lt;/h3&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;p style=&amp;quot;color:#cbd5e1;&amp;quot;&amp;gt;${marker.actionData?.text || 'Sem informações'}&amp;lt;/p&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            modal.style.display = 'flex';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        updateMarkersPosition() {&lt;br /&gt;
            this.markers.forEach(({ element, data }) =&amp;gt; {&lt;br /&gt;
                if (parseInt(element.getAttribute('data-floor')) === this.currentFloor) {&lt;br /&gt;
                    const zoomedX = data.x * this.currentZoom;&lt;br /&gt;
                    const zoomedY = data.y * this.currentZoom;&lt;br /&gt;
                    element.style.left = `${zoomedX}px`;&lt;br /&gt;
                    element.style.top = `${zoomedY}px`;&lt;br /&gt;
                    &lt;br /&gt;
                    const iconSize = Math.max(24, Math.floor(24 * this.currentZoom));&lt;br /&gt;
                    const iconDiv = element.querySelector('.mapa-marker-icon');&lt;br /&gt;
                    if (iconDiv) {&lt;br /&gt;
                        if (data.iconUrl &amp;amp;&amp;amp; data.iconUrl !== '') {&lt;br /&gt;
                            let img = iconDiv.querySelector('img');&lt;br /&gt;
                            if (img) {&lt;br /&gt;
                                img.style.width = `${iconSize}px`;&lt;br /&gt;
                                img.style.height = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        } else {&lt;br /&gt;
                            let icon = iconDiv.querySelector('i');&lt;br /&gt;
                            if (icon) {&lt;br /&gt;
                                icon.style.fontSize = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        centerMap() {&lt;br /&gt;
            setTimeout(() =&amp;gt; {&lt;br /&gt;
                const layersWidth = this.layersContainer.scrollWidth;&lt;br /&gt;
                const layersHeight = this.layersContainer.scrollHeight;&lt;br /&gt;
                const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
                const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
                if (layersWidth &amp;gt; 0 &amp;amp;&amp;amp; layersHeight &amp;gt; 0) {&lt;br /&gt;
                    this.viewport.scrollLeft = (layersWidth - viewportWidth) / 2;&lt;br /&gt;
                    this.viewport.scrollTop = (layersHeight - viewportHeight) / 2;&lt;br /&gt;
                }&lt;br /&gt;
            }, 100);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomIn() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomOut() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        applyZoomChange(oldZoom) {&lt;br /&gt;
            const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
            const centerX = rect.width / 2;&lt;br /&gt;
            const centerY = rect.height / 2;&lt;br /&gt;
            const scrollX = (this.viewport.scrollLeft + centerX) / oldZoom;&lt;br /&gt;
            const scrollY = (this.viewport.scrollTop + centerY) / oldZoom;&lt;br /&gt;
            &lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.viewport.scrollLeft = scrollX * this.currentZoom - centerX;&lt;br /&gt;
            this.viewport.scrollTop = scrollY * this.currentZoom - centerY;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        resetView() {&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            this.centerMap();&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        prevFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;gt; 0) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex - 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        nextFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;lt; layers.length - 1) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex + 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToFloor(floorId) {&lt;br /&gt;
            this.currentFloor = floorId;&lt;br /&gt;
            this.layers.forEach(layer =&amp;gt; {&lt;br /&gt;
                const layerFloor = parseInt(layer.getAttribute('data-floor'));&lt;br /&gt;
                layer.style.display = layerFloor === floorId ? 'block' : 'none';&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToCoordinates(x, y, zoom = null, floorId = null) {&lt;br /&gt;
            if (floorId !== null &amp;amp;&amp;amp; floorId !== this.currentFloor) {&lt;br /&gt;
                this.goToFloor(floorId);&lt;br /&gt;
                setTimeout(() =&amp;gt; this._centerOnPoint(x, y, zoom), 100);&lt;br /&gt;
            } else {&lt;br /&gt;
                this._centerOnPoint(x, y, zoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        _centerOnPoint(x, y, zoom) {&lt;br /&gt;
            if (zoom !== null &amp;amp;&amp;amp; zoom !== this.currentZoom) {&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                this.currentZoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    this.applyZoomChange(oldZoom);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
            const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
            let screenX = x * this.currentZoom;&lt;br /&gt;
            let screenY = y * this.currentZoom;&lt;br /&gt;
            let targetScrollX = screenX - (viewportWidth / 2);&lt;br /&gt;
            let targetScrollY = screenY - (viewportHeight / 2);&lt;br /&gt;
            &lt;br /&gt;
            this.viewport.scrollLeft = Math.max(0, targetScrollX);&lt;br /&gt;
            this.viewport.scrollTop = Math.max(0, targetScrollY);&lt;br /&gt;
        this.showTemporaryHighlight(x, y);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    showTemporaryHighlight(x, y) {&lt;br /&gt;
        const highlight = document.createElement('div');&lt;br /&gt;
        highlight.style.cssText = `&lt;br /&gt;
            position: absolute;&lt;br /&gt;
            width: 60px;&lt;br /&gt;
            height: 60px;&lt;br /&gt;
            left: ${(x + (this.currentFloor?.offsetX || 0)) * this.currentZoom}px;&lt;br /&gt;
            top: ${(y + (this.currentFloor?.offsetY || 0)) * this.currentZoom}px;&lt;br /&gt;
            transform: translate(-50%, -50%);&lt;br /&gt;
            background: radial-gradient(circle, rgba(255,215,0,0.8) 0%, rgba(255,215,0,0) 70%);&lt;br /&gt;
            border-radius: 50%;&lt;br /&gt;
            pointer-events: none;&lt;br /&gt;
            z-index: 200;&lt;br /&gt;
            animation: mapa-highlight-pulse 0.6s ease-out 3;&lt;br /&gt;
        `;&lt;br /&gt;
&lt;br /&gt;
        this.layersContainer.appendChild(highlight);&lt;br /&gt;
&lt;br /&gt;
        setTimeout(() =&amp;gt; {&lt;br /&gt;
            highlight.remove();&lt;br /&gt;
        }, 1800);&lt;br /&gt;
    }&lt;br /&gt;
        setupKeyboardShortcuts() {&lt;br /&gt;
            const handler = (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;&lt;br /&gt;
                switch (e.key) {&lt;br /&gt;
                    case '+': case '=': e.preventDefault(); this.zoomIn(); break;&lt;br /&gt;
                    case '-': case '_': e.preventDefault(); this.zoomOut(); break;&lt;br /&gt;
                    case 'r': case 'R': e.preventDefault(); this.resetView(); break;&lt;br /&gt;
                    case 'ArrowUp': case 'PageUp': e.preventDefault(); this.prevFloor(); break;&lt;br /&gt;
                    case 'ArrowDown': case 'PageDown': e.preventDefault(); this.nextFloor(); break;&lt;br /&gt;
                }&lt;br /&gt;
            };&lt;br /&gt;
            document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            this.keyHandler = handler;&lt;br /&gt;
            document.addEventListener('keydown', this.keyHandler);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showError(message) {&lt;br /&gt;
            this.container.innerHTML = `&amp;lt;div style=&amp;quot;display:flex; align-items:center; justify-content:center; height:100%; color:#ef4444; text-align:center; padding:20px;&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;div&amp;gt;❌ ${message}&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;/div&amp;gt;`;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        destroy() {&lt;br /&gt;
            if (this.keyHandler) {&lt;br /&gt;
                document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            }&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
(function() {&lt;br /&gt;
    var containerId = 'mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    var configJSON = &amp;lt;!--{$configJSON|default:'null'}--&amp;gt;;  // Recebe o JSON&lt;br /&gt;
    var urlJSON = '&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;';        // Recebe URL do JSON&lt;br /&gt;
    &lt;br /&gt;
    // Nome da variável global única para este mapa&lt;br /&gt;
    var globalVarName = 'mapaViewer_&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    &lt;br /&gt;
    function iniciarMapa() {&lt;br /&gt;
        var container = document.getElementById(containerId);&lt;br /&gt;
        if (!container) return;&lt;br /&gt;
        if (container.hasAttribute('data-iniciado')) return;&lt;br /&gt;
        &lt;br /&gt;
        container.setAttribute('data-iniciado', 'true');&lt;br /&gt;
        &lt;br /&gt;
        var viewer = null;&lt;br /&gt;
        &lt;br /&gt;
        if (configJSON &amp;amp;&amp;amp; configJSON !== 'null') {&lt;br /&gt;
            // Configuração inline&lt;br /&gt;
            viewer = new MapaViewer(containerId, configJSON);&lt;br /&gt;
        } else if (urlJSON) {&lt;br /&gt;
            // Configuração via URL&lt;br /&gt;
            fetch(urlJSON)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; {&lt;br /&gt;
                    window[globalVarName] = new MapaViewer(containerId, config);&lt;br /&gt;
                })&lt;br /&gt;
                .catch(error =&amp;gt; {&lt;br /&gt;
                    console.error('Erro ao carregar mapa:', error);&lt;br /&gt;
                    container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Erro ao carregar mapa: ' + error.message + '&amp;lt;/div&amp;gt;';&lt;br /&gt;
                });&lt;br /&gt;
            return; // Sai porque o fetch é assíncrono&lt;br /&gt;
        } else {&lt;br /&gt;
            container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Nenhuma configuração fornecida&amp;lt;/div&amp;gt;';&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Guarda a instância globalmente para acesso externo&lt;br /&gt;
        if (viewer) {&lt;br /&gt;
            window[globalVarName] = viewer;&lt;br /&gt;
            console.log('Mapa inicializado! Use window.' + globalVarName + ' para controlar');&lt;br /&gt;
            console.log('Exemplo: window.' + globalVarName + '.nextFloor()');&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (document.readyState === 'loading') {&lt;br /&gt;
        document.addEventListener('DOMContentLoaded', iniciarMapa);&lt;br /&gt;
    } else {&lt;br /&gt;
        iniciarMapa();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Ceu_azul&amp;diff=46025</id>
		<title>Usuário:Ceu azul</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Ceu_azul&amp;diff=46025"/>
		<updated>2026-04-11T17:10:41Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{MapaViewer&lt;br /&gt;
|id=meu_mapa&lt;br /&gt;
|largura=800px&lt;br /&gt;
|altura=600px&lt;br /&gt;
|configJSON={{#invoke:MapaJson|getDados|Mapa:BlackMarket}}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46024</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46024"/>
		<updated>2026-04-11T17:09:56Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
@keyframes mapa-highlight-pulse {&lt;br /&gt;
    0% {&lt;br /&gt;
        transform: translate(-50%, -50%) scale(0.5);&lt;br /&gt;
        opacity: 1;&lt;br /&gt;
    }&lt;br /&gt;
    100% {&lt;br /&gt;
        transform: translate(-50%, -50%) scale(2);&lt;br /&gt;
        opacity: 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mapa-highlight {&lt;br /&gt;
    animation: mapa-highlight-pulse 0.6s ease-out 3;&lt;br /&gt;
}&lt;br /&gt;
            .mapa-visualizador-padrao {&lt;br /&gt;
                border-width: 6px;&lt;br /&gt;
                border-style: solid;&lt;br /&gt;
                border-image: linear-gradient(to top, #412b22, #8a6d58) 1;&lt;br /&gt;
                border-radius: 10px;&lt;br /&gt;
                background: black;&lt;br /&gt;
                padding: 1pt;&lt;br /&gt;
                position: relative;&lt;br /&gt;
                display: inline-table;&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    position: absolute;&lt;br /&gt;
                    bottom: -13px;&lt;br /&gt;
                    right: 0px;&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    button {&lt;br /&gt;
                        width: 25px;&lt;br /&gt;
                        height: 25px;&lt;br /&gt;
                        border-radius: 50%;&lt;br /&gt;
                        border: 2px solid #5a4632;&lt;br /&gt;
                        background: radial-gradient(circle at 30% 30%, #a98a6a, #aa855f);&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 2px 3px rgba(255, 255, 255, 0.4),&lt;br /&gt;
                            inset 0 -3px 5px rgba(0, 0, 0, 0.6),&lt;br /&gt;
                            0 2px 3px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        cursor: pointer;&lt;br /&gt;
                        color: white;&lt;br /&gt;
                        font-weight: bolder;&lt;br /&gt;
                        margin-left: 5px;&lt;br /&gt;
                    }&lt;br /&gt;
                    /* Efeito pressionado */&lt;br /&gt;
                    button:active {&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 3px 6px rgba(0, 0, 0, 0.8),&lt;br /&gt;
                            0 1px 1px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        transform: translateY(1px);&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
        &amp;lt;/style&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;botoes-interativos&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;up&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.prevFloor()&amp;quot;&amp;gt;&amp;amp;uparrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;down&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.nextFloor()&amp;quot;&amp;gt;&amp;amp;downarrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;plus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomIn()&amp;quot;&amp;gt;+&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;minus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomOut()&amp;quot;&amp;gt;-&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu_mapa.goToCoordinates(246, 496, 1.5, 2)&amp;quot;&amp;gt;Ir para Tesouro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46023</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46023"/>
		<updated>2026-04-11T17:08:04Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
@keyframes mapa-highlight-pulse {&lt;br /&gt;
    0% {&lt;br /&gt;
        transform: translate(-50%, -50%) scale(0.5);&lt;br /&gt;
        opacity: 1;&lt;br /&gt;
    }&lt;br /&gt;
    100% {&lt;br /&gt;
        transform: translate(-50%, -50%) scale(2);&lt;br /&gt;
        opacity: 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mapa-highlight {&lt;br /&gt;
    animation: mapa-highlight-pulse 0.6s ease-out 3;&lt;br /&gt;
}&lt;br /&gt;
            .mapa-visualizador-padrao {&lt;br /&gt;
                border-width: 6px;&lt;br /&gt;
                border-style: solid;&lt;br /&gt;
                border-image: linear-gradient(to top, #412b22, #8a6d58) 1;&lt;br /&gt;
                border-radius: 10px;&lt;br /&gt;
                background: black;&lt;br /&gt;
                padding: 1pt;&lt;br /&gt;
                position: relative;&lt;br /&gt;
                display: inline-table;&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    position: absolute;&lt;br /&gt;
                    bottom: -13px;&lt;br /&gt;
                    right: 0px;&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    button {&lt;br /&gt;
                        width: 25px;&lt;br /&gt;
                        height: 25px;&lt;br /&gt;
                        border-radius: 50%;&lt;br /&gt;
                        border: 2px solid #5a4632;&lt;br /&gt;
                        background: radial-gradient(circle at 30% 30%, #a98a6a, #aa855f);&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 2px 3px rgba(255, 255, 255, 0.4),&lt;br /&gt;
                            inset 0 -3px 5px rgba(0, 0, 0, 0.6),&lt;br /&gt;
                            0 2px 3px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        cursor: pointer;&lt;br /&gt;
                        color: white;&lt;br /&gt;
                        font-weight: bolder;&lt;br /&gt;
                        margin-left: 5px;&lt;br /&gt;
                    }&lt;br /&gt;
                    /* Efeito pressionado */&lt;br /&gt;
                    button:active {&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 3px 6px rgba(0, 0, 0, 0.8),&lt;br /&gt;
                            0 1px 1px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        transform: translateY(1px);&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
        &amp;lt;/style&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;botoes-interativos&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;up&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.prevFloor()&amp;quot;&amp;gt;&amp;amp;uparrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;down&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.nextFloor()&amp;quot;&amp;gt;&amp;amp;downarrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;plus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomIn()&amp;quot;&amp;gt;+&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;minus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomOut()&amp;quot;&amp;gt;-&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu_mapa.goToCoordinates(300, 250, 1.5, 1)&amp;quot;&amp;gt;Ir para Tesouro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46022</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46022"/>
		<updated>2026-04-11T17:06:29Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
            .mapa-visualizador-padrao {&lt;br /&gt;
                border-width: 6px;&lt;br /&gt;
                border-style: solid;&lt;br /&gt;
                border-image: linear-gradient(to top, #412b22, #8a6d58) 1;&lt;br /&gt;
                border-radius: 10px;&lt;br /&gt;
                background: black;&lt;br /&gt;
                padding: 1pt;&lt;br /&gt;
                position: relative;&lt;br /&gt;
                display: inline-table;&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    position: absolute;&lt;br /&gt;
                    bottom: -13px;&lt;br /&gt;
                    right: 0px;&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    button {&lt;br /&gt;
                        width: 25px;&lt;br /&gt;
                        height: 25px;&lt;br /&gt;
                        border-radius: 50%;&lt;br /&gt;
                        border: 2px solid #5a4632;&lt;br /&gt;
                        background: radial-gradient(circle at 30% 30%, #a98a6a, #aa855f);&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 2px 3px rgba(255, 255, 255, 0.4),&lt;br /&gt;
                            inset 0 -3px 5px rgba(0, 0, 0, 0.6),&lt;br /&gt;
                            0 2px 3px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        cursor: pointer;&lt;br /&gt;
                        color: white;&lt;br /&gt;
                        font-weight: bolder;&lt;br /&gt;
                        margin-left: 5px;&lt;br /&gt;
                    }&lt;br /&gt;
                    /* Efeito pressionado */&lt;br /&gt;
                    button:active {&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 3px 6px rgba(0, 0, 0, 0.8),&lt;br /&gt;
                            0 1px 1px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        transform: translateY(1px);&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
        &amp;lt;/style&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;botoes-interativos&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;up&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.prevFloor()&amp;quot;&amp;gt;&amp;amp;uparrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;down&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.nextFloor()&amp;quot;&amp;gt;&amp;amp;downarrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;plus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomIn()&amp;quot;&amp;gt;+&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;minus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomOut()&amp;quot;&amp;gt;-&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu_mapa.goToCoordinates(300, 250, 1.5, 1)&amp;quot;&amp;gt;Ir para Tesouro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46021</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46021"/>
		<updated>2026-04-11T17:05:01Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
            .mapa-visualizador-padrao {&lt;br /&gt;
                border-width: 6px;&lt;br /&gt;
                border-style: solid;&lt;br /&gt;
                border-image: linear-gradient(to top, #412b22, #8a6d58) 1;&lt;br /&gt;
                border-radius: 10px;&lt;br /&gt;
                background: black;&lt;br /&gt;
                padding: 1pt;&lt;br /&gt;
                position: relative;&lt;br /&gt;
                display: inline-table;&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    position: absolute;&lt;br /&gt;
                    bottom: -13px;&lt;br /&gt;
                    right: 0px;&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    button {&lt;br /&gt;
                        width: 25px;&lt;br /&gt;
                        height: 25px;&lt;br /&gt;
                        border-radius: 50%;&lt;br /&gt;
                        border: 2px solid #5a4632;&lt;br /&gt;
                        background: radial-gradient(circle at 30% 30%, #a98a6a, #aa855f);&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 2px 3px rgba(255, 255, 255, 0.4),&lt;br /&gt;
                            inset 0 -3px 5px rgba(0, 0, 0, 0.6),&lt;br /&gt;
                            0 2px 3px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        cursor: pointer;&lt;br /&gt;
                        color: white;&lt;br /&gt;
                        font-weight: bolder;&lt;br /&gt;
                        margin-left: 5px;&lt;br /&gt;
                    }&lt;br /&gt;
                    /* Efeito pressionado */&lt;br /&gt;
                    button:active {&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 3px 6px rgba(0, 0, 0, 0.8),&lt;br /&gt;
                            0 1px 1px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        transform: translateY(1px);&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
        &amp;lt;/style&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;botoes-interativos&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;up&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.prevFloor()&amp;quot;&amp;gt;&amp;amp;uparrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;down&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.nextFloor()&amp;quot;&amp;gt;&amp;amp;downarrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;plus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomIn()&amp;quot;&amp;gt;+&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;minus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomOut()&amp;quot;&amp;gt;-&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button onclick=&amp;quot;window.mapaViewer_meu_mapa.goToCoordinates(300, 250, 1.5, 1)&amp;quot;&amp;gt;Ir para Tesouro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Ceu_azul&amp;diff=46020</id>
		<title>Usuário:Ceu azul</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Ceu_azul&amp;diff=46020"/>
		<updated>2026-04-11T16:57:51Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{MapaViewer&lt;br /&gt;
|id=meu_mapa&lt;br /&gt;
|largura=500px&lt;br /&gt;
|altura=500px&lt;br /&gt;
|configJSON={{#invoke:MapaJson|getDados|Mapa:BlackMarket}}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46019</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46019"/>
		<updated>2026-04-11T16:57:17Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
            .mapa-visualizador-padrao {&lt;br /&gt;
                border-width: 6px;&lt;br /&gt;
                border-style: solid;&lt;br /&gt;
                border-image: linear-gradient(to top, #412b22, #8a6d58) 1;&lt;br /&gt;
                border-radius: 10px;&lt;br /&gt;
                background: black;&lt;br /&gt;
                padding: 1pt;&lt;br /&gt;
                position: relative;&lt;br /&gt;
                display: inline-table;&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    position: absolute;&lt;br /&gt;
                    bottom: -13px;&lt;br /&gt;
                    right: 0px;&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                .botoes-interativos {&lt;br /&gt;
                    button {&lt;br /&gt;
                        width: 25px;&lt;br /&gt;
                        height: 25px;&lt;br /&gt;
                        border-radius: 50%;&lt;br /&gt;
                        border: 2px solid #5a4632;&lt;br /&gt;
                        background: radial-gradient(circle at 30% 30%, #a98a6a, #aa855f);&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 2px 3px rgba(255, 255, 255, 0.4),&lt;br /&gt;
                            inset 0 -3px 5px rgba(0, 0, 0, 0.6),&lt;br /&gt;
                            0 2px 3px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        cursor: pointer;&lt;br /&gt;
                        color: white;&lt;br /&gt;
                        font-weight: bolder;&lt;br /&gt;
                        margin-left: 5px;&lt;br /&gt;
                    }&lt;br /&gt;
                    /* Efeito pressionado */&lt;br /&gt;
                    button:active {&lt;br /&gt;
                        box-shadow:&lt;br /&gt;
                            inset 0 3px 6px rgba(0, 0, 0, 0.8),&lt;br /&gt;
                            0 1px 1px rgba(0, 0, 0, 0.5);&lt;br /&gt;
                        transform: translateY(1px);&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
        &amp;lt;/style&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;botoes-interativos&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;up&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.prevFloor()&amp;quot;&amp;gt;&amp;amp;uparrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;down&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.nextFloor()&amp;quot;&amp;gt;&amp;amp;downarrow;&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;plus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomIn()&amp;quot;&amp;gt;+&amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;minus&amp;quot; onclick=&amp;quot;window.mapaViewer_meu_mapa.zoomOut()&amp;quot;&amp;gt;-&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46018</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46018"/>
		<updated>2026-04-11T15:34:28Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
.mapa-visualizador-padrao {&lt;br /&gt;
  border: 10px double blue; /* A cor interna é o background, ou defina border-color */&lt;br /&gt;
  outline: 5px solid red; /&lt;br /&gt;
background: #cacaca;&lt;br /&gt;
padding: 10pt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46017</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46017"/>
		<updated>2026-04-11T15:29:57Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
#mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt; {&lt;br /&gt;
  border: 10px double blue; /* A cor interna é o background, ou defina border-color */&lt;br /&gt;
  outline: 5px solid red; /&lt;br /&gt;
background: #cacaca;&lt;br /&gt;
padding: 10pt;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46016</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46016"/>
		<updated>2026-04-11T15:26:44Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
#mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt; {&lt;br /&gt;
border: 3px solid red;&lt;br /&gt;
background: #cacaca;&lt;br /&gt;
padding: 10pt;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46015</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46015"/>
		<updated>2026-04-11T15:22:12Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
#mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt; {&lt;br /&gt;
border&amp;gt;3px solid red;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46014</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46014"/>
		<updated>2026-04-11T15:17:00Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=46013</id>
		<title>Widget:MapaViewer</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=46013"/>
		<updated>2026-04-11T15:16:41Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&amp;quot; style=&amp;quot;width: &amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;; height: &amp;lt;!--{$altura|escape:'quotes'|default:'500px'}--&amp;gt;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
class MapaViewer {&lt;br /&gt;
        constructor(containerId, configOrUrl) {&lt;br /&gt;
            this.container = document.getElementById(containerId);&lt;br /&gt;
            if (!this.container) {&lt;br /&gt;
                console.error(`Container &amp;quot;${containerId}&amp;quot; não encontrado`);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            this.config = null;&lt;br /&gt;
            this.currentZoom = 1;&lt;br /&gt;
            this.currentFloor = 0;&lt;br /&gt;
            this.minZoom = 0.3;&lt;br /&gt;
            this.maxZoom = 3;&lt;br /&gt;
            this.zoomStep = 0.1;&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            this.isPanning = false;&lt;br /&gt;
            this.panStart = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 };&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
&lt;br /&gt;
            this.init();&lt;br /&gt;
&lt;br /&gt;
            if (typeof configOrUrl === 'string') {&lt;br /&gt;
                if (configOrUrl.startsWith('http')) {&lt;br /&gt;
                    this.loadJSON(configOrUrl);&lt;br /&gt;
                } else {&lt;br /&gt;
                    try {&lt;br /&gt;
                        this.loadConfig(JSON.parse(configOrUrl));&lt;br /&gt;
                    } catch(e) {&lt;br /&gt;
                        console.error('JSON inválido:', e);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } else if (configOrUrl) {&lt;br /&gt;
                this.loadConfig(configOrUrl);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        init() {&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.container.style.position = 'relative';&lt;br /&gt;
            this.container.style.overflow = 'hidden';&lt;br /&gt;
            this.container.style.background = '#0f172a';&lt;br /&gt;
&lt;br /&gt;
            this.viewport = document.createElement('div');&lt;br /&gt;
            this.viewport.style.cssText = 'width:100%;height:100%;overflow:hidden;cursor:grab;position:relative;';&lt;br /&gt;
&lt;br /&gt;
            this.layersContainer = document.createElement('div');&lt;br /&gt;
            this.layersContainer.style.cssText = 'position:relative;min-width:100%;min-height:100%;';&lt;br /&gt;
&lt;br /&gt;
            this.viewport.appendChild(this.layersContainer);&lt;br /&gt;
            this.container.appendChild(this.viewport);&lt;br /&gt;
            this.bindEvents();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        bindEvents() {&lt;br /&gt;
            this.viewport.addEventListener('mousedown', (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.closest('.mapa-marker')) return;&lt;br /&gt;
                this.isPanning = true;&lt;br /&gt;
                this.panStart = {&lt;br /&gt;
                    x: e.clientX,&lt;br /&gt;
                    y: e.clientY,&lt;br /&gt;
                    scrollLeft: this.viewport.scrollLeft,&lt;br /&gt;
                    scrollTop: this.viewport.scrollTop&lt;br /&gt;
                };&lt;br /&gt;
                this.viewport.style.cursor = 'grabbing';&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mousemove', (e) =&amp;gt; {&lt;br /&gt;
                if (!this.isPanning) return;&lt;br /&gt;
                this.viewport.scrollLeft = this.panStart.scrollLeft - (e.clientX - this.panStart.x);&lt;br /&gt;
                this.viewport.scrollTop = this.panStart.scrollTop - (e.clientY - this.panStart.y);&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mouseup', () =&amp;gt; {&lt;br /&gt;
                this.isPanning = false;&lt;br /&gt;
                this.viewport.style.cursor = 'grab';&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            this.viewport.addEventListener('wheel', (e) =&amp;gt; {&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                if (e.deltaY &amp;lt; 0) {&lt;br /&gt;
                    this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
                } else {&lt;br /&gt;
                    this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
                }&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
                    const mouseX = e.clientX - rect.left;&lt;br /&gt;
                    const mouseY = e.clientY - rect.top;&lt;br /&gt;
                    const mapX = (this.viewport.scrollLeft + mouseX) / oldZoom;&lt;br /&gt;
                    const mapY = (this.viewport.scrollTop + mouseY) / oldZoom;&lt;br /&gt;
                    &lt;br /&gt;
                    document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                        img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                    });&lt;br /&gt;
                    &lt;br /&gt;
                    this.updateMarkersPosition();&lt;br /&gt;
                    this.viewport.scrollLeft = mapX * this.currentZoom - mouseX;&lt;br /&gt;
                    this.viewport.scrollTop = mapY * this.currentZoom - mouseY;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadConfig(config) {&lt;br /&gt;
            this.config = this.normalizeConfig(config);&lt;br /&gt;
            if (!this.config.layers || this.config.layers.length === 0) {&lt;br /&gt;
                console.error('Nenhuma camada encontrada');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            this.currentFloor = this.config.mapConfig?.initialFloor || 0;&lt;br /&gt;
            this.minZoom = this.config.mapConfig?.minZoom || 0.3;&lt;br /&gt;
            this.maxZoom = this.config.mapConfig?.maxZoom || 3;&lt;br /&gt;
            this.zoomStep = this.config.mapConfig?.zoomStep || 0.1;&lt;br /&gt;
            this.render();&lt;br /&gt;
            this.setupKeyboardShortcuts();&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadJSON(url) {&lt;br /&gt;
            fetch(url)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; this.loadConfig(config))&lt;br /&gt;
                .catch(error =&amp;gt; console.error('Erro ao carregar JSON:', error));&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        normalizeConfig(config) {&lt;br /&gt;
            if (!config.mapConfig) config.mapConfig = {};&lt;br /&gt;
            if (!config.layers) config.layers = [];&lt;br /&gt;
            config.layers.forEach((layer, idx) =&amp;gt; {&lt;br /&gt;
                if (layer.id === undefined) layer.id = idx;&lt;br /&gt;
                if (layer.name === undefined) layer.name = `Andar ${idx + 1}`;&lt;br /&gt;
                if (layer.imageUrl === undefined) layer.imageUrl = '';&lt;br /&gt;
                if (layer.offsetX === undefined) layer.offsetX = 0;&lt;br /&gt;
                if (layer.offsetY === undefined) layer.offsetY = 0;&lt;br /&gt;
                if (layer.scale === undefined || layer.scale === null) layer.scale = 1;&lt;br /&gt;
                if (layer.opacity === undefined || layer.opacity === null) layer.opacity = 100;&lt;br /&gt;
                if (!layer.markers) layer.markers = [];&lt;br /&gt;
                layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                    if (marker.x === undefined) marker.x = 0;&lt;br /&gt;
                    if (marker.y === undefined) marker.y = 0;&lt;br /&gt;
                    if (marker.hasBadge === undefined) marker.hasBadge = false;&lt;br /&gt;
                    if (marker.action === undefined) marker.action = 'popup';&lt;br /&gt;
                    if (!marker.actionData) marker.actionData = {};&lt;br /&gt;
                    if (marker.iconUrl === undefined) marker.iconUrl = '';&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            return config;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        render() {&lt;br /&gt;
            if (!this.config || !this.config.layers.length) {&lt;br /&gt;
                this.showError('Nenhum dado para renderizar');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.layersContainer.innerHTML = '';&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            &lt;br /&gt;
            this.config.layers.forEach((layer) =&amp;gt; {&lt;br /&gt;
                const $layer = document.createElement('div');&lt;br /&gt;
                $layer.className = 'mapa-layer';&lt;br /&gt;
                $layer.setAttribute('data-floor', layer.id);&lt;br /&gt;
                $layer.style.display = layer.id === this.currentFloor ? 'block' : 'none';&lt;br /&gt;
                $layer.style.position = 'absolute';&lt;br /&gt;
                $layer.style.top = '0';&lt;br /&gt;
                $layer.style.left = '0';&lt;br /&gt;
                $layer.style.transform = `translate(${layer.offsetX}px, ${layer.offsetY}px)`;&lt;br /&gt;
                $layer.style.opacity = (layer.opacity || 100) / 100;&lt;br /&gt;
                &lt;br /&gt;
                const $img = document.createElement('img');&lt;br /&gt;
                $img.className = 'mapa-image';&lt;br /&gt;
                $img.style.display = 'block';&lt;br /&gt;
                $img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                $img.style.transformOrigin = '0 0';&lt;br /&gt;
                $img.style.pointerEvents = 'none';&lt;br /&gt;
                $img.src = layer.imageUrl || '';&lt;br /&gt;
                &lt;br /&gt;
                $img.onerror = () =&amp;gt; {&lt;br /&gt;
                    const canvas = document.createElement('canvas');&lt;br /&gt;
                    canvas.width = 800;&lt;br /&gt;
                    canvas.height = 600;&lt;br /&gt;
                    const ctx = canvas.getContext('2d');&lt;br /&gt;
                    ctx.fillStyle = '#334155';&lt;br /&gt;
                    ctx.fillRect(0, 0, canvas.width, canvas.height);&lt;br /&gt;
                    ctx.fillStyle = 'white';&lt;br /&gt;
                    ctx.font = '20px Arial';&lt;br /&gt;
                    ctx.fillText(layer.name || 'Andar', 50, 100);&lt;br /&gt;
                    $img.src = canvas.toDataURL();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $img.onload = () =&amp;gt; {&lt;br /&gt;
                    this.layersContainer.style.width = `${$img.width}px`;&lt;br /&gt;
                    this.layersContainer.style.height = `${$img.height}px`;&lt;br /&gt;
                    this.centerMap();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($img);&lt;br /&gt;
                &lt;br /&gt;
                const $markersContainer = document.createElement('div');&lt;br /&gt;
                $markersContainer.style.position = 'absolute';&lt;br /&gt;
                $markersContainer.style.top = '0';&lt;br /&gt;
                $markersContainer.style.left = '0';&lt;br /&gt;
                $markersContainer.style.width = '100%';&lt;br /&gt;
                $markersContainer.style.height = '100%';&lt;br /&gt;
                $markersContainer.style.pointerEvents = 'none';&lt;br /&gt;
                &lt;br /&gt;
                if (layer.markers &amp;amp;&amp;amp; layer.markers.length) {&lt;br /&gt;
                    layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                        const $marker = this.createMarker(marker, layer.id);&lt;br /&gt;
                        $markersContainer.appendChild($marker);&lt;br /&gt;
                        this.markers.push({ element: $marker, data: marker });&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($markersContainer);&lt;br /&gt;
                this.layersContainer.appendChild($layer);&lt;br /&gt;
                this.layers.push($layer);&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.isReady = true;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        createMarker(marker, floorId) {&lt;br /&gt;
            const $marker = document.createElement('div');&lt;br /&gt;
            $marker.className = 'mapa-marker';&lt;br /&gt;
            $marker.setAttribute('data-marker-id', marker.id);&lt;br /&gt;
            $marker.setAttribute('data-floor', floorId);&lt;br /&gt;
            $marker.style.position = 'absolute';&lt;br /&gt;
            $marker.style.left = `${marker.x * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.top = `${marker.y * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.zIndex = '100';&lt;br /&gt;
            $marker.style.transform = 'translate(-50%, -50%)';&lt;br /&gt;
            $marker.style.pointerEvents = 'auto';&lt;br /&gt;
            &lt;br /&gt;
            const hasAction = marker.action &amp;amp;&amp;amp; marker.action !== 'none';&lt;br /&gt;
            $marker.style.cursor = hasAction ? 'pointer' : 'default';&lt;br /&gt;
            &lt;br /&gt;
            const iconSize = Math.max(16, Math.floor(16 * this.currentZoom));&lt;br /&gt;
            let iconHtml = '';&lt;br /&gt;
            &lt;br /&gt;
            if (marker.iconUrl &amp;amp;&amp;amp; marker.iconUrl !== '') {&lt;br /&gt;
                iconHtml = `&amp;lt;img src=&amp;quot;${marker.iconUrl}&amp;quot; style=&amp;quot;width:${iconSize}px; height:${iconSize}px; object-fit:contain;&amp;quot;&amp;gt;`;&lt;br /&gt;
            } else {&lt;br /&gt;
                iconHtml = `&amp;lt;i class=&amp;quot;fas fa-map-marker-alt&amp;quot; style=&amp;quot;font-size:${iconSize}px;&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;`;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            $marker.innerHTML = `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-icon&amp;quot; style=&amp;quot;display:flex; align-items:center; justify-content:center; pointer-events:none;&amp;quot;&amp;gt;${iconHtml}&amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-tooltip&amp;quot; style=&amp;quot;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;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;strong&amp;gt;${marker.name || 'Marcador'}&amp;lt;/strong&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseenter', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1.1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '1';&lt;br /&gt;
                    tooltip.style.visibility = 'visible';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseleave', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '0';&lt;br /&gt;
                    tooltip.style.visibility = 'hidden';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            if (hasAction) {&lt;br /&gt;
                $marker.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    e.stopPropagation();&lt;br /&gt;
                    this.executeAction(marker);&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            return $marker;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        executeAction(marker) {&lt;br /&gt;
            switch (marker.action) {&lt;br /&gt;
                case 'popup':&lt;br /&gt;
                    this.showPopup(marker);&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'nextFloor':&lt;br /&gt;
                    this.nextFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'prevFloor':&lt;br /&gt;
                    this.prevFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'gotoFloor':&lt;br /&gt;
                    if (marker.actionData?.floorId !== undefined) {&lt;br /&gt;
                        this.goToFloor(marker.actionData.floorId);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'link':&lt;br /&gt;
                    if (marker.actionData?.url) {&lt;br /&gt;
                        window.open(marker.actionData.url, marker.actionData.target || '_blank');&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showPopup(marker) {&lt;br /&gt;
            let modal = document.getElementById('mapa-viewer-modal');&lt;br /&gt;
            if (!modal) {&lt;br /&gt;
                modal = document.createElement('div');&lt;br /&gt;
                modal.id = 'mapa-viewer-modal';&lt;br /&gt;
                modal.style.cssText = 'display:none; position:fixed; z-index:10000; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.7); justify-content:center; align-items:center;';&lt;br /&gt;
                modal.innerHTML = `&lt;br /&gt;
                    &amp;lt;div style=&amp;quot;background:#1e293b; border-radius:16px; max-width:350px; width:90%; padding:20px; position:relative; border:1px solid #334155;&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;span style=&amp;quot;position:absolute; right:15px; top:10px; font-size:24px; cursor:pointer; color:#94a3b8;&amp;quot;&amp;gt;&amp;amp;times;&amp;lt;/span&amp;gt;&lt;br /&gt;
                        &amp;lt;div id=&amp;quot;mapa-viewer-popup-content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                `;&lt;br /&gt;
                document.body.appendChild(modal);&lt;br /&gt;
                modal.querySelector('span').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
                    modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
                modal.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    if (e.target === modal) modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const content = modal.querySelector('#mapa-viewer-popup-content');&lt;br /&gt;
            content.innerHTML = `&lt;br /&gt;
                &amp;lt;div style=&amp;quot;display:flex; align-items:center; margin-bottom:12px;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;h3 style=&amp;quot;margin:0; color:#a5b4fc;&amp;quot;&amp;gt;${marker.name}&amp;lt;/h3&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;p style=&amp;quot;color:#cbd5e1;&amp;quot;&amp;gt;${marker.actionData?.text || 'Sem informações'}&amp;lt;/p&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            modal.style.display = 'flex';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        updateMarkersPosition() {&lt;br /&gt;
            this.markers.forEach(({ element, data }) =&amp;gt; {&lt;br /&gt;
                if (parseInt(element.getAttribute('data-floor')) === this.currentFloor) {&lt;br /&gt;
                    const zoomedX = data.x * this.currentZoom;&lt;br /&gt;
                    const zoomedY = data.y * this.currentZoom;&lt;br /&gt;
                    element.style.left = `${zoomedX}px`;&lt;br /&gt;
                    element.style.top = `${zoomedY}px`;&lt;br /&gt;
                    &lt;br /&gt;
                    const iconSize = Math.max(24, Math.floor(24 * this.currentZoom));&lt;br /&gt;
                    const iconDiv = element.querySelector('.mapa-marker-icon');&lt;br /&gt;
                    if (iconDiv) {&lt;br /&gt;
                        if (data.iconUrl &amp;amp;&amp;amp; data.iconUrl !== '') {&lt;br /&gt;
                            let img = iconDiv.querySelector('img');&lt;br /&gt;
                            if (img) {&lt;br /&gt;
                                img.style.width = `${iconSize}px`;&lt;br /&gt;
                                img.style.height = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        } else {&lt;br /&gt;
                            let icon = iconDiv.querySelector('i');&lt;br /&gt;
                            if (icon) {&lt;br /&gt;
                                icon.style.fontSize = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        centerMap() {&lt;br /&gt;
            setTimeout(() =&amp;gt; {&lt;br /&gt;
                const layersWidth = this.layersContainer.scrollWidth;&lt;br /&gt;
                const layersHeight = this.layersContainer.scrollHeight;&lt;br /&gt;
                const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
                const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
                if (layersWidth &amp;gt; 0 &amp;amp;&amp;amp; layersHeight &amp;gt; 0) {&lt;br /&gt;
                    this.viewport.scrollLeft = (layersWidth - viewportWidth) / 2;&lt;br /&gt;
                    this.viewport.scrollTop = (layersHeight - viewportHeight) / 2;&lt;br /&gt;
                }&lt;br /&gt;
            }, 100);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomIn() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomOut() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        applyZoomChange(oldZoom) {&lt;br /&gt;
            const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
            const centerX = rect.width / 2;&lt;br /&gt;
            const centerY = rect.height / 2;&lt;br /&gt;
            const scrollX = (this.viewport.scrollLeft + centerX) / oldZoom;&lt;br /&gt;
            const scrollY = (this.viewport.scrollTop + centerY) / oldZoom;&lt;br /&gt;
            &lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.viewport.scrollLeft = scrollX * this.currentZoom - centerX;&lt;br /&gt;
            this.viewport.scrollTop = scrollY * this.currentZoom - centerY;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        resetView() {&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            this.centerMap();&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        prevFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;gt; 0) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex - 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        nextFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;lt; layers.length - 1) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex + 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToFloor(floorId) {&lt;br /&gt;
            this.currentFloor = floorId;&lt;br /&gt;
            this.layers.forEach(layer =&amp;gt; {&lt;br /&gt;
                const layerFloor = parseInt(layer.getAttribute('data-floor'));&lt;br /&gt;
                layer.style.display = layerFloor === floorId ? 'block' : 'none';&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToCoordinates(x, y, zoom = null, floorId = null) {&lt;br /&gt;
            if (floorId !== null &amp;amp;&amp;amp; floorId !== this.currentFloor) {&lt;br /&gt;
                this.goToFloor(floorId);&lt;br /&gt;
                setTimeout(() =&amp;gt; this._centerOnPoint(x, y, zoom), 100);&lt;br /&gt;
            } else {&lt;br /&gt;
                this._centerOnPoint(x, y, zoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        _centerOnPoint(x, y, zoom) {&lt;br /&gt;
            if (zoom !== null &amp;amp;&amp;amp; zoom !== this.currentZoom) {&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                this.currentZoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    this.applyZoomChange(oldZoom);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
            const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
            let screenX = x * this.currentZoom;&lt;br /&gt;
            let screenY = y * this.currentZoom;&lt;br /&gt;
            let targetScrollX = screenX - (viewportWidth / 2);&lt;br /&gt;
            let targetScrollY = screenY - (viewportHeight / 2);&lt;br /&gt;
            &lt;br /&gt;
            this.viewport.scrollLeft = Math.max(0, targetScrollX);&lt;br /&gt;
            this.viewport.scrollTop = Math.max(0, targetScrollY);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        setupKeyboardShortcuts() {&lt;br /&gt;
            const handler = (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;&lt;br /&gt;
                switch (e.key) {&lt;br /&gt;
                    case '+': case '=': e.preventDefault(); this.zoomIn(); break;&lt;br /&gt;
                    case '-': case '_': e.preventDefault(); this.zoomOut(); break;&lt;br /&gt;
                    case 'r': case 'R': e.preventDefault(); this.resetView(); break;&lt;br /&gt;
                    case 'ArrowUp': case 'PageUp': e.preventDefault(); this.prevFloor(); break;&lt;br /&gt;
                    case 'ArrowDown': case 'PageDown': e.preventDefault(); this.nextFloor(); break;&lt;br /&gt;
                }&lt;br /&gt;
            };&lt;br /&gt;
            document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            this.keyHandler = handler;&lt;br /&gt;
            document.addEventListener('keydown', this.keyHandler);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showError(message) {&lt;br /&gt;
            this.container.innerHTML = `&amp;lt;div style=&amp;quot;display:flex; align-items:center; justify-content:center; height:100%; color:#ef4444; text-align:center; padding:20px;&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;div&amp;gt;❌ ${message}&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;/div&amp;gt;`;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        destroy() {&lt;br /&gt;
            if (this.keyHandler) {&lt;br /&gt;
                document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            }&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
(function() {&lt;br /&gt;
    var containerId = 'mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    var configJSON = &amp;lt;!--{$configJSON|default:'null'}--&amp;gt;;  // Recebe o JSON&lt;br /&gt;
    var urlJSON = '&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;';        // Recebe URL do JSON&lt;br /&gt;
    &lt;br /&gt;
    // Nome da variável global única para este mapa&lt;br /&gt;
    var globalVarName = 'mapaViewer_&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    &lt;br /&gt;
    function iniciarMapa() {&lt;br /&gt;
        var container = document.getElementById(containerId);&lt;br /&gt;
        if (!container) return;&lt;br /&gt;
        if (container.hasAttribute('data-iniciado')) return;&lt;br /&gt;
        &lt;br /&gt;
        container.setAttribute('data-iniciado', 'true');&lt;br /&gt;
        &lt;br /&gt;
        var viewer = null;&lt;br /&gt;
        &lt;br /&gt;
        if (configJSON &amp;amp;&amp;amp; configJSON !== 'null') {&lt;br /&gt;
            // Configuração inline&lt;br /&gt;
            viewer = new MapaViewer(containerId, configJSON);&lt;br /&gt;
        } else if (urlJSON) {&lt;br /&gt;
            // Configuração via URL&lt;br /&gt;
            fetch(urlJSON)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; {&lt;br /&gt;
                    window[globalVarName] = new MapaViewer(containerId, config);&lt;br /&gt;
                })&lt;br /&gt;
                .catch(error =&amp;gt; {&lt;br /&gt;
                    console.error('Erro ao carregar mapa:', error);&lt;br /&gt;
                    container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Erro ao carregar mapa: ' + error.message + '&amp;lt;/div&amp;gt;';&lt;br /&gt;
                });&lt;br /&gt;
            return; // Sai porque o fetch é assíncrono&lt;br /&gt;
        } else {&lt;br /&gt;
            container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Nenhuma configuração fornecida&amp;lt;/div&amp;gt;';&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Guarda a instância globalmente para acesso externo&lt;br /&gt;
        if (viewer) {&lt;br /&gt;
            window[globalVarName] = viewer;&lt;br /&gt;
            console.log('Mapa inicializado! Use window.' + globalVarName + ' para controlar');&lt;br /&gt;
            console.log('Exemplo: window.' + globalVarName + '.nextFloor()');&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (document.readyState === 'loading') {&lt;br /&gt;
        document.addEventListener('DOMContentLoaded', iniciarMapa);&lt;br /&gt;
    } else {&lt;br /&gt;
        iniciarMapa();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Predefini%C3%A7%C3%A3o:MapaViewer&amp;diff=46012</id>
		<title>Predefinição:MapaViewer</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Predefini%C3%A7%C3%A3o:MapaViewer&amp;diff=46012"/>
		<updated>2026-04-11T15:15:55Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;mapa-visualizador-padrao&amp;quot;&amp;gt;&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id={{{id|}}}&lt;br /&gt;
|largura={{{largura|100%}}}&lt;br /&gt;
|altura={{{altura|500px}}}&lt;br /&gt;
|configJSON={{{configJSON|}}}&lt;br /&gt;
|url={{{url|}}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{#widget:MapaControle&lt;br /&gt;
|id={{{id|}}}&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46011</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46011"/>
		<updated>2026-04-11T15:14:29Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id}--&amp;gt;.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46010</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46010"/>
		<updated>2026-04-11T15:12:47Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{{id|}}}&lt;br /&gt;
&amp;lt;!--{$id}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;margin-top: 20px;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46009</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46009"/>
		<updated>2026-04-11T15:11:21Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{{id|}}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;margin-top: 20px;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46008</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46008"/>
		<updated>2026-04-11T14:58:07Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id=&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
|largura=&amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;&lt;br /&gt;
|altura=&amp;lt;!--{$altura|escape:'quotes'|default:'500px'}--&amp;gt;&lt;br /&gt;
|configJSON=&amp;lt;!--{$configJSON|default:'null'}--&amp;gt;&lt;br /&gt;
|url=&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46007</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46007"/>
		<updated>2026-04-11T14:57:16Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id=&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
|largura=&amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;&lt;br /&gt;
|altura=&amp;lt;!--{$altura|escape:'quotes'|default:'500px'}--&amp;gt;&lt;br /&gt;
|configJSON=&amp;lt;!--{$configJSON|default:'null'}--&amp;gt;&lt;br /&gt;
|url=&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46006</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46006"/>
		<updated>2026-04-11T14:56:26Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id=&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
|largura=&amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;&lt;br /&gt;
|altura=&amp;lt;!--{$altura|escape:'quotes'|default:'500px'}&lt;br /&gt;
|configJSON=&amp;lt;!--{$configJSON|default:'null'}--&amp;gt;&lt;br /&gt;
|url=&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46005</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46005"/>
		<updated>2026-04-11T14:55:44Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id=&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
|largura=&amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;&lt;br /&gt;
|altura={&amp;lt;!--{$altura|escape:'quotes'|default:'500px'}&lt;br /&gt;
|configJSON=&amp;lt;!--{$configJSON|default:'null'}--&amp;gt;&lt;br /&gt;
|url=&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46004</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46004"/>
		<updated>2026-04-11T14:53:36Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id={$id}&lt;br /&gt;
|largura={$largura}&lt;br /&gt;
|altura={$altura}&lt;br /&gt;
|configJSON={$configJSON}&lt;br /&gt;
|url={$url}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46003</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46003"/>
		<updated>2026-04-11T14:52:54Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{$id}&lt;br /&gt;
{{$id}}&lt;br /&gt;
{{{$id}}}&lt;br /&gt;
&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id={$id}&lt;br /&gt;
|largura={$largura}&lt;br /&gt;
|altura={$altura}&lt;br /&gt;
|configJSON={$configJSON}&lt;br /&gt;
|url={$url}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46002</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46002"/>
		<updated>2026-04-11T14:52:06Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{$id}&lt;br /&gt;
&lt;br /&gt;
{{#widget:MapaViewer&lt;br /&gt;
|id={$id}&lt;br /&gt;
|largura={$largura}&lt;br /&gt;
|altura={$altura}&lt;br /&gt;
|configJSON={$configJSON}&lt;br /&gt;
|url={$url}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46001</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46001"/>
		<updated>2026-04-11T14:46:52Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MapaViewer&lt;br /&gt;
|id={$id}&lt;br /&gt;
|largura={$largura}&lt;br /&gt;
|altura={$altura}&lt;br /&gt;
|configJSON={$configJSON}&lt;br /&gt;
|url={$url}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46000</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=46000"/>
		<updated>2026-04-11T14:45:36Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MapaViewer&lt;br /&gt;
|id={$id|}&lt;br /&gt;
|largura={$largura|100%}&lt;br /&gt;
|altura={$altura|500px}&lt;br /&gt;
|configJSON={$configJSON|}&lt;br /&gt;
|url={$url|}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=45999</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=45999"/>
		<updated>2026-04-11T14:35:52Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MapaViewer&lt;br /&gt;
|id={{{id|}}}&lt;br /&gt;
|largura={{{largura|100%}}}&lt;br /&gt;
|altura={{{altura|500px}}}&lt;br /&gt;
|configJSON={{{configJSON|}}}&lt;br /&gt;
|url={{{url|}}}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Predefini%C3%A7%C3%A3o:MapaViewer&amp;diff=45998</id>
		<title>Predefinição:MapaViewer</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Predefini%C3%A7%C3%A3o:MapaViewer&amp;diff=45998"/>
		<updated>2026-04-11T14:33:38Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MapaControle&lt;br /&gt;
|id={{{id|}}}&lt;br /&gt;
|largura={{{largura|100%}}}&lt;br /&gt;
|altura={{{altura|500px}}}&lt;br /&gt;
|configJSON={{{configJSON|}}}&lt;br /&gt;
|url={{{url|}}}&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=45997</id>
		<title>Widget:MapaControle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaControle&amp;diff=45997"/>
		<updated>2026-04-11T14:33:03Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Criou página com '{{#widget:MapaViewer}}'&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MapaViewer}}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Mapa:BlackMarket&amp;diff=45996</id>
		<title>Mapa:BlackMarket</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Mapa:BlackMarket&amp;diff=45996"/>
		<updated>2026-04-11T14:25:51Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
  &amp;quot;mapConfig&amp;quot;: {&lt;br /&gt;
    &amp;quot;initialFloor&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;defaultZoom&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;minZoom&amp;quot;: 0.3,&lt;br /&gt;
    &amp;quot;maxZoom&amp;quot;: 3,&lt;br /&gt;
    &amp;quot;zoomStep&amp;quot;: 0.1&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;layers&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Andar&amp;quot;,&lt;br /&gt;
      &amp;quot;imageUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/b/b7/Black_market_andar01.png&amp;quot;,&lt;br /&gt;
      &amp;quot;offsetX&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;offsetY&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;scale&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;opacity&amp;quot;: 100,&lt;br /&gt;
      &amp;quot;markers&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913455257&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 641.25,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 385,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913456593&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 641.25,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 257.5,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Andar&amp;quot;,&lt;br /&gt;
      &amp;quot;imageUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/2/22/Black_market_terreo.png&amp;quot;,&lt;br /&gt;
      &amp;quot;offsetX&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;offsetY&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;scale&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;opacity&amp;quot;: 100,&lt;br /&gt;
      &amp;quot;markers&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913428226&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 245,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 490,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913431714&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 580,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 538,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913440529&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 693.076923076923,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 489.9999999999999,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913443737&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 804.6153846153844,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 539.2307692307692,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913449649&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 1157.2727272727273,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 506.3636363636363,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913482097&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 638.8888888888889,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 385.55555555555554,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913483337&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 641.1111111111111,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 255.55555555555554,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 2,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Andar&amp;quot;,&lt;br /&gt;
      &amp;quot;imageUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/d/d8/Black_market_subsolo01.png&amp;quot;,&lt;br /&gt;
      &amp;quot;offsetX&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;offsetY&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;scale&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;opacity&amp;quot;: 100,&lt;br /&gt;
      &amp;quot;markers&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913462330&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 693.75,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 508.75,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913463385&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 711.25,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 511.25,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913466441&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 725,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 510,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913469345&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 1172.5,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 510,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913474121&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 245.55555555555554,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 496.66666666666663,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=M%C3%B3dulo:MapaJson&amp;diff=45995</id>
		<title>Módulo:MapaJson</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=M%C3%B3dulo:MapaJson&amp;diff=45995"/>
		<updated>2026-04-11T14:24:20Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
function p.getDados(frame)&lt;br /&gt;
    local pageName = frame.args[1] or ''&lt;br /&gt;
    &lt;br /&gt;
    if pageName == '' then&lt;br /&gt;
        return ''&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    local title = mw.title.new(pageName)&lt;br /&gt;
    if not title or not title.exists then&lt;br /&gt;
        return ''&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    local jsonContent = title:getContent() or ''&lt;br /&gt;
    &lt;br /&gt;
    -- Apenas remover espaços extras&lt;br /&gt;
    jsonContent = jsonContent:gsub(&amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
    jsonContent = jsonContent:gsub(&amp;quot;\r&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
    jsonContent = jsonContent:gsub(&amp;quot;\t&amp;quot;, &amp;quot; &amp;quot;)&lt;br /&gt;
    jsonContent = jsonContent:gsub(&amp;quot;  +&amp;quot;, &amp;quot; &amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
    return jsonContent&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=M%C3%B3dulo:MapaJson&amp;diff=45994</id>
		<title>Módulo:MapaJson</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=M%C3%B3dulo:MapaJson&amp;diff=45994"/>
		<updated>2026-04-11T14:19:24Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
function p.getDados(frame)&lt;br /&gt;
    -- Carrega o conteúdo da página JSON&lt;br /&gt;
    local pageName = frame.args[1] or ''&lt;br /&gt;
    local conteudo = mw.title.new(pageName):getContent()&lt;br /&gt;
    &lt;br /&gt;
    -- Transforma o texto JSON em uma tabela Lua&lt;br /&gt;
    local dados = mw.text.jsonDecode(conteudo)&lt;br /&gt;
    &lt;br /&gt;
    -- Exemplo: Pegar um valor específico chamado 'item'&lt;br /&gt;
    -- Você adapta isso conforme a estrutura do seu JSON&lt;br /&gt;
    return dados&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=M%C3%B3dulo:MapaJson&amp;diff=45993</id>
		<title>Módulo:MapaJson</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=M%C3%B3dulo:MapaJson&amp;diff=45993"/>
		<updated>2026-04-11T14:16:16Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
function p.getDados(frame)&lt;br /&gt;
    -- Carrega o conteúdo da página JSON&lt;br /&gt;
    local titulo = &amp;quot;Mapa:Blackmarket&amp;quot;&lt;br /&gt;
    local conteudo = mw.title.new(titulo):getContent()&lt;br /&gt;
    &lt;br /&gt;
    -- Transforma o texto JSON em uma tabela Lua&lt;br /&gt;
    local dados = mw.text.jsonDecode(conteudo)&lt;br /&gt;
    &lt;br /&gt;
    -- Exemplo: Pegar um valor específico chamado 'item'&lt;br /&gt;
    -- Você adapta isso conforme a estrutura do seu JSON&lt;br /&gt;
    return dados&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=45992</id>
		<title>Widget:MapaViewer</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=45992"/>
		<updated>2026-04-11T13:53:36Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&amp;quot; style=&amp;quot;width: &amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;; height: &amp;lt;!--{$altura|escape:'quotes'|default:'500px'}--&amp;gt;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_{{{id|}}}.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
class MapaViewer {&lt;br /&gt;
        constructor(containerId, configOrUrl) {&lt;br /&gt;
            this.container = document.getElementById(containerId);&lt;br /&gt;
            if (!this.container) {&lt;br /&gt;
                console.error(`Container &amp;quot;${containerId}&amp;quot; não encontrado`);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            this.config = null;&lt;br /&gt;
            this.currentZoom = 1;&lt;br /&gt;
            this.currentFloor = 0;&lt;br /&gt;
            this.minZoom = 0.3;&lt;br /&gt;
            this.maxZoom = 3;&lt;br /&gt;
            this.zoomStep = 0.1;&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            this.isPanning = false;&lt;br /&gt;
            this.panStart = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 };&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
&lt;br /&gt;
            this.init();&lt;br /&gt;
&lt;br /&gt;
            if (typeof configOrUrl === 'string') {&lt;br /&gt;
                if (configOrUrl.startsWith('http')) {&lt;br /&gt;
                    this.loadJSON(configOrUrl);&lt;br /&gt;
                } else {&lt;br /&gt;
                    try {&lt;br /&gt;
                        this.loadConfig(JSON.parse(configOrUrl));&lt;br /&gt;
                    } catch(e) {&lt;br /&gt;
                        console.error('JSON inválido:', e);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } else if (configOrUrl) {&lt;br /&gt;
                this.loadConfig(configOrUrl);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        init() {&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.container.style.position = 'relative';&lt;br /&gt;
            this.container.style.overflow = 'hidden';&lt;br /&gt;
            this.container.style.background = '#0f172a';&lt;br /&gt;
&lt;br /&gt;
            this.viewport = document.createElement('div');&lt;br /&gt;
            this.viewport.style.cssText = 'width:100%;height:100%;overflow:hidden;cursor:grab;position:relative;';&lt;br /&gt;
&lt;br /&gt;
            this.layersContainer = document.createElement('div');&lt;br /&gt;
            this.layersContainer.style.cssText = 'position:relative;min-width:100%;min-height:100%;';&lt;br /&gt;
&lt;br /&gt;
            this.viewport.appendChild(this.layersContainer);&lt;br /&gt;
            this.container.appendChild(this.viewport);&lt;br /&gt;
            this.bindEvents();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        bindEvents() {&lt;br /&gt;
            this.viewport.addEventListener('mousedown', (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.closest('.mapa-marker')) return;&lt;br /&gt;
                this.isPanning = true;&lt;br /&gt;
                this.panStart = {&lt;br /&gt;
                    x: e.clientX,&lt;br /&gt;
                    y: e.clientY,&lt;br /&gt;
                    scrollLeft: this.viewport.scrollLeft,&lt;br /&gt;
                    scrollTop: this.viewport.scrollTop&lt;br /&gt;
                };&lt;br /&gt;
                this.viewport.style.cursor = 'grabbing';&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mousemove', (e) =&amp;gt; {&lt;br /&gt;
                if (!this.isPanning) return;&lt;br /&gt;
                this.viewport.scrollLeft = this.panStart.scrollLeft - (e.clientX - this.panStart.x);&lt;br /&gt;
                this.viewport.scrollTop = this.panStart.scrollTop - (e.clientY - this.panStart.y);&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mouseup', () =&amp;gt; {&lt;br /&gt;
                this.isPanning = false;&lt;br /&gt;
                this.viewport.style.cursor = 'grab';&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            this.viewport.addEventListener('wheel', (e) =&amp;gt; {&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                if (e.deltaY &amp;lt; 0) {&lt;br /&gt;
                    this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
                } else {&lt;br /&gt;
                    this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
                }&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
                    const mouseX = e.clientX - rect.left;&lt;br /&gt;
                    const mouseY = e.clientY - rect.top;&lt;br /&gt;
                    const mapX = (this.viewport.scrollLeft + mouseX) / oldZoom;&lt;br /&gt;
                    const mapY = (this.viewport.scrollTop + mouseY) / oldZoom;&lt;br /&gt;
                    &lt;br /&gt;
                    document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                        img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                    });&lt;br /&gt;
                    &lt;br /&gt;
                    this.updateMarkersPosition();&lt;br /&gt;
                    this.viewport.scrollLeft = mapX * this.currentZoom - mouseX;&lt;br /&gt;
                    this.viewport.scrollTop = mapY * this.currentZoom - mouseY;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadConfig(config) {&lt;br /&gt;
            this.config = this.normalizeConfig(config);&lt;br /&gt;
            if (!this.config.layers || this.config.layers.length === 0) {&lt;br /&gt;
                console.error('Nenhuma camada encontrada');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            this.currentFloor = this.config.mapConfig?.initialFloor || 0;&lt;br /&gt;
            this.minZoom = this.config.mapConfig?.minZoom || 0.3;&lt;br /&gt;
            this.maxZoom = this.config.mapConfig?.maxZoom || 3;&lt;br /&gt;
            this.zoomStep = this.config.mapConfig?.zoomStep || 0.1;&lt;br /&gt;
            this.render();&lt;br /&gt;
            this.setupKeyboardShortcuts();&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadJSON(url) {&lt;br /&gt;
            fetch(url)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; this.loadConfig(config))&lt;br /&gt;
                .catch(error =&amp;gt; console.error('Erro ao carregar JSON:', error));&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        normalizeConfig(config) {&lt;br /&gt;
            if (!config.mapConfig) config.mapConfig = {};&lt;br /&gt;
            if (!config.layers) config.layers = [];&lt;br /&gt;
            config.layers.forEach((layer, idx) =&amp;gt; {&lt;br /&gt;
                if (layer.id === undefined) layer.id = idx;&lt;br /&gt;
                if (layer.name === undefined) layer.name = `Andar ${idx + 1}`;&lt;br /&gt;
                if (layer.imageUrl === undefined) layer.imageUrl = '';&lt;br /&gt;
                if (layer.offsetX === undefined) layer.offsetX = 0;&lt;br /&gt;
                if (layer.offsetY === undefined) layer.offsetY = 0;&lt;br /&gt;
                if (layer.scale === undefined || layer.scale === null) layer.scale = 1;&lt;br /&gt;
                if (layer.opacity === undefined || layer.opacity === null) layer.opacity = 100;&lt;br /&gt;
                if (!layer.markers) layer.markers = [];&lt;br /&gt;
                layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                    if (marker.x === undefined) marker.x = 0;&lt;br /&gt;
                    if (marker.y === undefined) marker.y = 0;&lt;br /&gt;
                    if (marker.hasBadge === undefined) marker.hasBadge = false;&lt;br /&gt;
                    if (marker.action === undefined) marker.action = 'popup';&lt;br /&gt;
                    if (!marker.actionData) marker.actionData = {};&lt;br /&gt;
                    if (marker.iconUrl === undefined) marker.iconUrl = '';&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            return config;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        render() {&lt;br /&gt;
            if (!this.config || !this.config.layers.length) {&lt;br /&gt;
                this.showError('Nenhum dado para renderizar');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.layersContainer.innerHTML = '';&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            &lt;br /&gt;
            this.config.layers.forEach((layer) =&amp;gt; {&lt;br /&gt;
                const $layer = document.createElement('div');&lt;br /&gt;
                $layer.className = 'mapa-layer';&lt;br /&gt;
                $layer.setAttribute('data-floor', layer.id);&lt;br /&gt;
                $layer.style.display = layer.id === this.currentFloor ? 'block' : 'none';&lt;br /&gt;
                $layer.style.position = 'absolute';&lt;br /&gt;
                $layer.style.top = '0';&lt;br /&gt;
                $layer.style.left = '0';&lt;br /&gt;
                $layer.style.transform = `translate(${layer.offsetX}px, ${layer.offsetY}px)`;&lt;br /&gt;
                $layer.style.opacity = (layer.opacity || 100) / 100;&lt;br /&gt;
                &lt;br /&gt;
                const $img = document.createElement('img');&lt;br /&gt;
                $img.className = 'mapa-image';&lt;br /&gt;
                $img.style.display = 'block';&lt;br /&gt;
                $img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                $img.style.transformOrigin = '0 0';&lt;br /&gt;
                $img.style.pointerEvents = 'none';&lt;br /&gt;
                $img.src = layer.imageUrl || '';&lt;br /&gt;
                &lt;br /&gt;
                $img.onerror = () =&amp;gt; {&lt;br /&gt;
                    const canvas = document.createElement('canvas');&lt;br /&gt;
                    canvas.width = 800;&lt;br /&gt;
                    canvas.height = 600;&lt;br /&gt;
                    const ctx = canvas.getContext('2d');&lt;br /&gt;
                    ctx.fillStyle = '#334155';&lt;br /&gt;
                    ctx.fillRect(0, 0, canvas.width, canvas.height);&lt;br /&gt;
                    ctx.fillStyle = 'white';&lt;br /&gt;
                    ctx.font = '20px Arial';&lt;br /&gt;
                    ctx.fillText(layer.name || 'Andar', 50, 100);&lt;br /&gt;
                    $img.src = canvas.toDataURL();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $img.onload = () =&amp;gt; {&lt;br /&gt;
                    this.layersContainer.style.width = `${$img.width}px`;&lt;br /&gt;
                    this.layersContainer.style.height = `${$img.height}px`;&lt;br /&gt;
                    this.centerMap();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($img);&lt;br /&gt;
                &lt;br /&gt;
                const $markersContainer = document.createElement('div');&lt;br /&gt;
                $markersContainer.style.position = 'absolute';&lt;br /&gt;
                $markersContainer.style.top = '0';&lt;br /&gt;
                $markersContainer.style.left = '0';&lt;br /&gt;
                $markersContainer.style.width = '100%';&lt;br /&gt;
                $markersContainer.style.height = '100%';&lt;br /&gt;
                $markersContainer.style.pointerEvents = 'none';&lt;br /&gt;
                &lt;br /&gt;
                if (layer.markers &amp;amp;&amp;amp; layer.markers.length) {&lt;br /&gt;
                    layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                        const $marker = this.createMarker(marker, layer.id);&lt;br /&gt;
                        $markersContainer.appendChild($marker);&lt;br /&gt;
                        this.markers.push({ element: $marker, data: marker });&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($markersContainer);&lt;br /&gt;
                this.layersContainer.appendChild($layer);&lt;br /&gt;
                this.layers.push($layer);&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.isReady = true;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        createMarker(marker, floorId) {&lt;br /&gt;
            const $marker = document.createElement('div');&lt;br /&gt;
            $marker.className = 'mapa-marker';&lt;br /&gt;
            $marker.setAttribute('data-marker-id', marker.id);&lt;br /&gt;
            $marker.setAttribute('data-floor', floorId);&lt;br /&gt;
            $marker.style.position = 'absolute';&lt;br /&gt;
            $marker.style.left = `${marker.x * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.top = `${marker.y * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.zIndex = '100';&lt;br /&gt;
            $marker.style.transform = 'translate(-50%, -50%)';&lt;br /&gt;
            $marker.style.pointerEvents = 'auto';&lt;br /&gt;
            &lt;br /&gt;
            const hasAction = marker.action &amp;amp;&amp;amp; marker.action !== 'none';&lt;br /&gt;
            $marker.style.cursor = hasAction ? 'pointer' : 'default';&lt;br /&gt;
            &lt;br /&gt;
            const iconSize = Math.max(16, Math.floor(16 * this.currentZoom));&lt;br /&gt;
            let iconHtml = '';&lt;br /&gt;
            &lt;br /&gt;
            if (marker.iconUrl &amp;amp;&amp;amp; marker.iconUrl !== '') {&lt;br /&gt;
                iconHtml = `&amp;lt;img src=&amp;quot;${marker.iconUrl}&amp;quot; style=&amp;quot;width:${iconSize}px; height:${iconSize}px; object-fit:contain;&amp;quot;&amp;gt;`;&lt;br /&gt;
            } else {&lt;br /&gt;
                iconHtml = `&amp;lt;i class=&amp;quot;fas fa-map-marker-alt&amp;quot; style=&amp;quot;font-size:${iconSize}px;&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;`;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            $marker.innerHTML = `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-icon&amp;quot; style=&amp;quot;display:flex; align-items:center; justify-content:center; pointer-events:none;&amp;quot;&amp;gt;${iconHtml}&amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-tooltip&amp;quot; style=&amp;quot;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;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;strong&amp;gt;${marker.name || 'Marcador'}&amp;lt;/strong&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseenter', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1.1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '1';&lt;br /&gt;
                    tooltip.style.visibility = 'visible';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseleave', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '0';&lt;br /&gt;
                    tooltip.style.visibility = 'hidden';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            if (hasAction) {&lt;br /&gt;
                $marker.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    e.stopPropagation();&lt;br /&gt;
                    this.executeAction(marker);&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            return $marker;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        executeAction(marker) {&lt;br /&gt;
            switch (marker.action) {&lt;br /&gt;
                case 'popup':&lt;br /&gt;
                    this.showPopup(marker);&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'nextFloor':&lt;br /&gt;
                    this.nextFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'prevFloor':&lt;br /&gt;
                    this.prevFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'gotoFloor':&lt;br /&gt;
                    if (marker.actionData?.floorId !== undefined) {&lt;br /&gt;
                        this.goToFloor(marker.actionData.floorId);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'link':&lt;br /&gt;
                    if (marker.actionData?.url) {&lt;br /&gt;
                        window.open(marker.actionData.url, marker.actionData.target || '_blank');&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showPopup(marker) {&lt;br /&gt;
            let modal = document.getElementById('mapa-viewer-modal');&lt;br /&gt;
            if (!modal) {&lt;br /&gt;
                modal = document.createElement('div');&lt;br /&gt;
                modal.id = 'mapa-viewer-modal';&lt;br /&gt;
                modal.style.cssText = 'display:none; position:fixed; z-index:10000; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.7); justify-content:center; align-items:center;';&lt;br /&gt;
                modal.innerHTML = `&lt;br /&gt;
                    &amp;lt;div style=&amp;quot;background:#1e293b; border-radius:16px; max-width:350px; width:90%; padding:20px; position:relative; border:1px solid #334155;&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;span style=&amp;quot;position:absolute; right:15px; top:10px; font-size:24px; cursor:pointer; color:#94a3b8;&amp;quot;&amp;gt;&amp;amp;times;&amp;lt;/span&amp;gt;&lt;br /&gt;
                        &amp;lt;div id=&amp;quot;mapa-viewer-popup-content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                `;&lt;br /&gt;
                document.body.appendChild(modal);&lt;br /&gt;
                modal.querySelector('span').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
                    modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
                modal.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    if (e.target === modal) modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const content = modal.querySelector('#mapa-viewer-popup-content');&lt;br /&gt;
            content.innerHTML = `&lt;br /&gt;
                &amp;lt;div style=&amp;quot;display:flex; align-items:center; margin-bottom:12px;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;h3 style=&amp;quot;margin:0; color:#a5b4fc;&amp;quot;&amp;gt;${marker.name}&amp;lt;/h3&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;p style=&amp;quot;color:#cbd5e1;&amp;quot;&amp;gt;${marker.actionData?.text || 'Sem informações'}&amp;lt;/p&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            modal.style.display = 'flex';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        updateMarkersPosition() {&lt;br /&gt;
            this.markers.forEach(({ element, data }) =&amp;gt; {&lt;br /&gt;
                if (parseInt(element.getAttribute('data-floor')) === this.currentFloor) {&lt;br /&gt;
                    const zoomedX = data.x * this.currentZoom;&lt;br /&gt;
                    const zoomedY = data.y * this.currentZoom;&lt;br /&gt;
                    element.style.left = `${zoomedX}px`;&lt;br /&gt;
                    element.style.top = `${zoomedY}px`;&lt;br /&gt;
                    &lt;br /&gt;
                    const iconSize = Math.max(24, Math.floor(24 * this.currentZoom));&lt;br /&gt;
                    const iconDiv = element.querySelector('.mapa-marker-icon');&lt;br /&gt;
                    if (iconDiv) {&lt;br /&gt;
                        if (data.iconUrl &amp;amp;&amp;amp; data.iconUrl !== '') {&lt;br /&gt;
                            let img = iconDiv.querySelector('img');&lt;br /&gt;
                            if (img) {&lt;br /&gt;
                                img.style.width = `${iconSize}px`;&lt;br /&gt;
                                img.style.height = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        } else {&lt;br /&gt;
                            let icon = iconDiv.querySelector('i');&lt;br /&gt;
                            if (icon) {&lt;br /&gt;
                                icon.style.fontSize = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        centerMap() {&lt;br /&gt;
            setTimeout(() =&amp;gt; {&lt;br /&gt;
                const layersWidth = this.layersContainer.scrollWidth;&lt;br /&gt;
                const layersHeight = this.layersContainer.scrollHeight;&lt;br /&gt;
                const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
                const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
                if (layersWidth &amp;gt; 0 &amp;amp;&amp;amp; layersHeight &amp;gt; 0) {&lt;br /&gt;
                    this.viewport.scrollLeft = (layersWidth - viewportWidth) / 2;&lt;br /&gt;
                    this.viewport.scrollTop = (layersHeight - viewportHeight) / 2;&lt;br /&gt;
                }&lt;br /&gt;
            }, 100);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomIn() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomOut() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        applyZoomChange(oldZoom) {&lt;br /&gt;
            const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
            const centerX = rect.width / 2;&lt;br /&gt;
            const centerY = rect.height / 2;&lt;br /&gt;
            const scrollX = (this.viewport.scrollLeft + centerX) / oldZoom;&lt;br /&gt;
            const scrollY = (this.viewport.scrollTop + centerY) / oldZoom;&lt;br /&gt;
            &lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.viewport.scrollLeft = scrollX * this.currentZoom - centerX;&lt;br /&gt;
            this.viewport.scrollTop = scrollY * this.currentZoom - centerY;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        resetView() {&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            this.centerMap();&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        prevFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;gt; 0) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex - 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        nextFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;lt; layers.length - 1) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex + 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToFloor(floorId) {&lt;br /&gt;
            this.currentFloor = floorId;&lt;br /&gt;
            this.layers.forEach(layer =&amp;gt; {&lt;br /&gt;
                const layerFloor = parseInt(layer.getAttribute('data-floor'));&lt;br /&gt;
                layer.style.display = layerFloor === floorId ? 'block' : 'none';&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToCoordinates(x, y, zoom = null, floorId = null) {&lt;br /&gt;
            if (floorId !== null &amp;amp;&amp;amp; floorId !== this.currentFloor) {&lt;br /&gt;
                this.goToFloor(floorId);&lt;br /&gt;
                setTimeout(() =&amp;gt; this._centerOnPoint(x, y, zoom), 100);&lt;br /&gt;
            } else {&lt;br /&gt;
                this._centerOnPoint(x, y, zoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        _centerOnPoint(x, y, zoom) {&lt;br /&gt;
            if (zoom !== null &amp;amp;&amp;amp; zoom !== this.currentZoom) {&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                this.currentZoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    this.applyZoomChange(oldZoom);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
            const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
            let screenX = x * this.currentZoom;&lt;br /&gt;
            let screenY = y * this.currentZoom;&lt;br /&gt;
            let targetScrollX = screenX - (viewportWidth / 2);&lt;br /&gt;
            let targetScrollY = screenY - (viewportHeight / 2);&lt;br /&gt;
            &lt;br /&gt;
            this.viewport.scrollLeft = Math.max(0, targetScrollX);&lt;br /&gt;
            this.viewport.scrollTop = Math.max(0, targetScrollY);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        setupKeyboardShortcuts() {&lt;br /&gt;
            const handler = (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;&lt;br /&gt;
                switch (e.key) {&lt;br /&gt;
                    case '+': case '=': e.preventDefault(); this.zoomIn(); break;&lt;br /&gt;
                    case '-': case '_': e.preventDefault(); this.zoomOut(); break;&lt;br /&gt;
                    case 'r': case 'R': e.preventDefault(); this.resetView(); break;&lt;br /&gt;
                    case 'ArrowUp': case 'PageUp': e.preventDefault(); this.prevFloor(); break;&lt;br /&gt;
                    case 'ArrowDown': case 'PageDown': e.preventDefault(); this.nextFloor(); break;&lt;br /&gt;
                }&lt;br /&gt;
            };&lt;br /&gt;
            document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            this.keyHandler = handler;&lt;br /&gt;
            document.addEventListener('keydown', this.keyHandler);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showError(message) {&lt;br /&gt;
            this.container.innerHTML = `&amp;lt;div style=&amp;quot;display:flex; align-items:center; justify-content:center; height:100%; color:#ef4444; text-align:center; padding:20px;&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;div&amp;gt;❌ ${message}&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;/div&amp;gt;`;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        destroy() {&lt;br /&gt;
            if (this.keyHandler) {&lt;br /&gt;
                document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            }&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
(function() {&lt;br /&gt;
    var containerId = 'mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    var configJSON = &amp;lt;!--{$configJSON|default:'null'}--&amp;gt;;  // Recebe o JSON&lt;br /&gt;
    var urlJSON = '&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;';        // Recebe URL do JSON&lt;br /&gt;
    &lt;br /&gt;
    // Nome da variável global única para este mapa&lt;br /&gt;
    var globalVarName = 'mapaViewer_&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    &lt;br /&gt;
    function iniciarMapa() {&lt;br /&gt;
        var container = document.getElementById(containerId);&lt;br /&gt;
        if (!container) return;&lt;br /&gt;
        if (container.hasAttribute('data-iniciado')) return;&lt;br /&gt;
        &lt;br /&gt;
        container.setAttribute('data-iniciado', 'true');&lt;br /&gt;
        &lt;br /&gt;
        var viewer = null;&lt;br /&gt;
        &lt;br /&gt;
        if (configJSON &amp;amp;&amp;amp; configJSON !== 'null') {&lt;br /&gt;
            // Configuração inline&lt;br /&gt;
            viewer = new MapaViewer(containerId, configJSON);&lt;br /&gt;
        } else if (urlJSON) {&lt;br /&gt;
            // Configuração via URL&lt;br /&gt;
            fetch(urlJSON)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; {&lt;br /&gt;
                    window[globalVarName] = new MapaViewer(containerId, config);&lt;br /&gt;
                })&lt;br /&gt;
                .catch(error =&amp;gt; {&lt;br /&gt;
                    console.error('Erro ao carregar mapa:', error);&lt;br /&gt;
                    container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Erro ao carregar mapa: ' + error.message + '&amp;lt;/div&amp;gt;';&lt;br /&gt;
                });&lt;br /&gt;
            return; // Sai porque o fetch é assíncrono&lt;br /&gt;
        } else {&lt;br /&gt;
            container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Nenhuma configuração fornecida&amp;lt;/div&amp;gt;';&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Guarda a instância globalmente para acesso externo&lt;br /&gt;
        if (viewer) {&lt;br /&gt;
            window[globalVarName] = viewer;&lt;br /&gt;
            console.log('Mapa inicializado! Use window.' + globalVarName + ' para controlar');&lt;br /&gt;
            console.log('Exemplo: window.' + globalVarName + '.nextFloor()');&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (document.readyState === 'loading') {&lt;br /&gt;
        document.addEventListener('DOMContentLoaded', iniciarMapa);&lt;br /&gt;
    } else {&lt;br /&gt;
        iniciarMapa();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Predefini%C3%A7%C3%A3o:MapaViewer&amp;diff=45991</id>
		<title>Predefinição:MapaViewer</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Predefini%C3%A7%C3%A3o:MapaViewer&amp;diff=45991"/>
		<updated>2026-04-11T13:40:51Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MapaViewer&lt;br /&gt;
|id={{{id|}}}&lt;br /&gt;
|largura={{{largura|100%}}}&lt;br /&gt;
|altura={{{altura|500px}}}&lt;br /&gt;
|configJSON={{{configJSON|}}}&lt;br /&gt;
|url={{{url|}}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Seus botões personalizados --&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;margin-top: 20px;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_{{{id|}}}.prevFloor()&amp;quot;&amp;gt;⬆️ Subir&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_{{{id|}}}.nextFloor()&amp;quot;&amp;gt;⬇️ Descer&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.zoomIn()&amp;quot;&amp;gt;🔍 +&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.zoomOut()&amp;quot;&amp;gt;🔍 -&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.goToFloor(0)&amp;quot;&amp;gt;🎯 Ir para Térreo&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;button onclick=&amp;quot;window.mapaViewer_meu-mapa.goToFloor(1)&amp;quot;&amp;gt;🎯 Ir para Primeiro&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=45990</id>
		<title>Widget:MapaViewer</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:MapaViewer&amp;diff=45990"/>
		<updated>2026-04-11T13:38:24Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;&amp;quot; style=&amp;quot;width: &amp;lt;!--{$largura|escape:'quotes'|default:'100%'}--&amp;gt;; height: &amp;lt;!--{$altura|escape:'quotes'|default:'500px'}--&amp;gt;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
class MapaViewer {&lt;br /&gt;
        constructor(containerId, configOrUrl) {&lt;br /&gt;
            this.container = document.getElementById(containerId);&lt;br /&gt;
            if (!this.container) {&lt;br /&gt;
                console.error(`Container &amp;quot;${containerId}&amp;quot; não encontrado`);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            this.config = null;&lt;br /&gt;
            this.currentZoom = 1;&lt;br /&gt;
            this.currentFloor = 0;&lt;br /&gt;
            this.minZoom = 0.3;&lt;br /&gt;
            this.maxZoom = 3;&lt;br /&gt;
            this.zoomStep = 0.1;&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            this.isPanning = false;&lt;br /&gt;
            this.panStart = { x: 0, y: 0, scrollLeft: 0, scrollTop: 0 };&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
&lt;br /&gt;
            this.init();&lt;br /&gt;
&lt;br /&gt;
            if (typeof configOrUrl === 'string') {&lt;br /&gt;
                if (configOrUrl.startsWith('http')) {&lt;br /&gt;
                    this.loadJSON(configOrUrl);&lt;br /&gt;
                } else {&lt;br /&gt;
                    try {&lt;br /&gt;
                        this.loadConfig(JSON.parse(configOrUrl));&lt;br /&gt;
                    } catch(e) {&lt;br /&gt;
                        console.error('JSON inválido:', e);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } else if (configOrUrl) {&lt;br /&gt;
                this.loadConfig(configOrUrl);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        init() {&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.container.style.position = 'relative';&lt;br /&gt;
            this.container.style.overflow = 'hidden';&lt;br /&gt;
            this.container.style.background = '#0f172a';&lt;br /&gt;
&lt;br /&gt;
            this.viewport = document.createElement('div');&lt;br /&gt;
            this.viewport.style.cssText = 'width:100%;height:100%;overflow:hidden;cursor:grab;position:relative;';&lt;br /&gt;
&lt;br /&gt;
            this.layersContainer = document.createElement('div');&lt;br /&gt;
            this.layersContainer.style.cssText = 'position:relative;min-width:100%;min-height:100%;';&lt;br /&gt;
&lt;br /&gt;
            this.viewport.appendChild(this.layersContainer);&lt;br /&gt;
            this.container.appendChild(this.viewport);&lt;br /&gt;
            this.bindEvents();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        bindEvents() {&lt;br /&gt;
            this.viewport.addEventListener('mousedown', (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.closest('.mapa-marker')) return;&lt;br /&gt;
                this.isPanning = true;&lt;br /&gt;
                this.panStart = {&lt;br /&gt;
                    x: e.clientX,&lt;br /&gt;
                    y: e.clientY,&lt;br /&gt;
                    scrollLeft: this.viewport.scrollLeft,&lt;br /&gt;
                    scrollTop: this.viewport.scrollTop&lt;br /&gt;
                };&lt;br /&gt;
                this.viewport.style.cursor = 'grabbing';&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mousemove', (e) =&amp;gt; {&lt;br /&gt;
                if (!this.isPanning) return;&lt;br /&gt;
                this.viewport.scrollLeft = this.panStart.scrollLeft - (e.clientX - this.panStart.x);&lt;br /&gt;
                this.viewport.scrollTop = this.panStart.scrollTop - (e.clientY - this.panStart.y);&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            window.addEventListener('mouseup', () =&amp;gt; {&lt;br /&gt;
                this.isPanning = false;&lt;br /&gt;
                this.viewport.style.cursor = 'grab';&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            this.viewport.addEventListener('wheel', (e) =&amp;gt; {&lt;br /&gt;
                e.preventDefault();&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                if (e.deltaY &amp;lt; 0) {&lt;br /&gt;
                    this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
                } else {&lt;br /&gt;
                    this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
                }&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
                    const mouseX = e.clientX - rect.left;&lt;br /&gt;
                    const mouseY = e.clientY - rect.top;&lt;br /&gt;
                    const mapX = (this.viewport.scrollLeft + mouseX) / oldZoom;&lt;br /&gt;
                    const mapY = (this.viewport.scrollTop + mouseY) / oldZoom;&lt;br /&gt;
                    &lt;br /&gt;
                    document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                        img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                    });&lt;br /&gt;
                    &lt;br /&gt;
                    this.updateMarkersPosition();&lt;br /&gt;
                    this.viewport.scrollLeft = mapX * this.currentZoom - mouseX;&lt;br /&gt;
                    this.viewport.scrollTop = mapY * this.currentZoom - mouseY;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadConfig(config) {&lt;br /&gt;
            this.config = this.normalizeConfig(config);&lt;br /&gt;
            if (!this.config.layers || this.config.layers.length === 0) {&lt;br /&gt;
                console.error('Nenhuma camada encontrada');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            this.currentFloor = this.config.mapConfig?.initialFloor || 0;&lt;br /&gt;
            this.minZoom = this.config.mapConfig?.minZoom || 0.3;&lt;br /&gt;
            this.maxZoom = this.config.mapConfig?.maxZoom || 3;&lt;br /&gt;
            this.zoomStep = this.config.mapConfig?.zoomStep || 0.1;&lt;br /&gt;
            this.render();&lt;br /&gt;
            this.setupKeyboardShortcuts();&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        loadJSON(url) {&lt;br /&gt;
            fetch(url)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; this.loadConfig(config))&lt;br /&gt;
                .catch(error =&amp;gt; console.error('Erro ao carregar JSON:', error));&lt;br /&gt;
            return this;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        normalizeConfig(config) {&lt;br /&gt;
            if (!config.mapConfig) config.mapConfig = {};&lt;br /&gt;
            if (!config.layers) config.layers = [];&lt;br /&gt;
            config.layers.forEach((layer, idx) =&amp;gt; {&lt;br /&gt;
                if (layer.id === undefined) layer.id = idx;&lt;br /&gt;
                if (layer.name === undefined) layer.name = `Andar ${idx + 1}`;&lt;br /&gt;
                if (layer.imageUrl === undefined) layer.imageUrl = '';&lt;br /&gt;
                if (layer.offsetX === undefined) layer.offsetX = 0;&lt;br /&gt;
                if (layer.offsetY === undefined) layer.offsetY = 0;&lt;br /&gt;
                if (layer.scale === undefined || layer.scale === null) layer.scale = 1;&lt;br /&gt;
                if (layer.opacity === undefined || layer.opacity === null) layer.opacity = 100;&lt;br /&gt;
                if (!layer.markers) layer.markers = [];&lt;br /&gt;
                layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                    if (marker.x === undefined) marker.x = 0;&lt;br /&gt;
                    if (marker.y === undefined) marker.y = 0;&lt;br /&gt;
                    if (marker.hasBadge === undefined) marker.hasBadge = false;&lt;br /&gt;
                    if (marker.action === undefined) marker.action = 'popup';&lt;br /&gt;
                    if (!marker.actionData) marker.actionData = {};&lt;br /&gt;
                    if (marker.iconUrl === undefined) marker.iconUrl = '';&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            return config;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        render() {&lt;br /&gt;
            if (!this.config || !this.config.layers.length) {&lt;br /&gt;
                this.showError('Nenhum dado para renderizar');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            this.layersContainer.innerHTML = '';&lt;br /&gt;
            this.layers = [];&lt;br /&gt;
            this.markers = [];&lt;br /&gt;
            &lt;br /&gt;
            this.config.layers.forEach((layer) =&amp;gt; {&lt;br /&gt;
                const $layer = document.createElement('div');&lt;br /&gt;
                $layer.className = 'mapa-layer';&lt;br /&gt;
                $layer.setAttribute('data-floor', layer.id);&lt;br /&gt;
                $layer.style.display = layer.id === this.currentFloor ? 'block' : 'none';&lt;br /&gt;
                $layer.style.position = 'absolute';&lt;br /&gt;
                $layer.style.top = '0';&lt;br /&gt;
                $layer.style.left = '0';&lt;br /&gt;
                $layer.style.transform = `translate(${layer.offsetX}px, ${layer.offsetY}px)`;&lt;br /&gt;
                $layer.style.opacity = (layer.opacity || 100) / 100;&lt;br /&gt;
                &lt;br /&gt;
                const $img = document.createElement('img');&lt;br /&gt;
                $img.className = 'mapa-image';&lt;br /&gt;
                $img.style.display = 'block';&lt;br /&gt;
                $img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
                $img.style.transformOrigin = '0 0';&lt;br /&gt;
                $img.style.pointerEvents = 'none';&lt;br /&gt;
                $img.src = layer.imageUrl || '';&lt;br /&gt;
                &lt;br /&gt;
                $img.onerror = () =&amp;gt; {&lt;br /&gt;
                    const canvas = document.createElement('canvas');&lt;br /&gt;
                    canvas.width = 800;&lt;br /&gt;
                    canvas.height = 600;&lt;br /&gt;
                    const ctx = canvas.getContext('2d');&lt;br /&gt;
                    ctx.fillStyle = '#334155';&lt;br /&gt;
                    ctx.fillRect(0, 0, canvas.width, canvas.height);&lt;br /&gt;
                    ctx.fillStyle = 'white';&lt;br /&gt;
                    ctx.font = '20px Arial';&lt;br /&gt;
                    ctx.fillText(layer.name || 'Andar', 50, 100);&lt;br /&gt;
                    $img.src = canvas.toDataURL();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $img.onload = () =&amp;gt; {&lt;br /&gt;
                    this.layersContainer.style.width = `${$img.width}px`;&lt;br /&gt;
                    this.layersContainer.style.height = `${$img.height}px`;&lt;br /&gt;
                    this.centerMap();&lt;br /&gt;
                };&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($img);&lt;br /&gt;
                &lt;br /&gt;
                const $markersContainer = document.createElement('div');&lt;br /&gt;
                $markersContainer.style.position = 'absolute';&lt;br /&gt;
                $markersContainer.style.top = '0';&lt;br /&gt;
                $markersContainer.style.left = '0';&lt;br /&gt;
                $markersContainer.style.width = '100%';&lt;br /&gt;
                $markersContainer.style.height = '100%';&lt;br /&gt;
                $markersContainer.style.pointerEvents = 'none';&lt;br /&gt;
                &lt;br /&gt;
                if (layer.markers &amp;amp;&amp;amp; layer.markers.length) {&lt;br /&gt;
                    layer.markers.forEach(marker =&amp;gt; {&lt;br /&gt;
                        const $marker = this.createMarker(marker, layer.id);&lt;br /&gt;
                        $markersContainer.appendChild($marker);&lt;br /&gt;
                        this.markers.push({ element: $marker, data: marker });&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                $layer.appendChild($markersContainer);&lt;br /&gt;
                this.layersContainer.appendChild($layer);&lt;br /&gt;
                this.layers.push($layer);&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.isReady = true;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        createMarker(marker, floorId) {&lt;br /&gt;
            const $marker = document.createElement('div');&lt;br /&gt;
            $marker.className = 'mapa-marker';&lt;br /&gt;
            $marker.setAttribute('data-marker-id', marker.id);&lt;br /&gt;
            $marker.setAttribute('data-floor', floorId);&lt;br /&gt;
            $marker.style.position = 'absolute';&lt;br /&gt;
            $marker.style.left = `${marker.x * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.top = `${marker.y * this.currentZoom}px`;&lt;br /&gt;
            $marker.style.zIndex = '100';&lt;br /&gt;
            $marker.style.transform = 'translate(-50%, -50%)';&lt;br /&gt;
            $marker.style.pointerEvents = 'auto';&lt;br /&gt;
            &lt;br /&gt;
            const hasAction = marker.action &amp;amp;&amp;amp; marker.action !== 'none';&lt;br /&gt;
            $marker.style.cursor = hasAction ? 'pointer' : 'default';&lt;br /&gt;
            &lt;br /&gt;
            const iconSize = Math.max(16, Math.floor(16 * this.currentZoom));&lt;br /&gt;
            let iconHtml = '';&lt;br /&gt;
            &lt;br /&gt;
            if (marker.iconUrl &amp;amp;&amp;amp; marker.iconUrl !== '') {&lt;br /&gt;
                iconHtml = `&amp;lt;img src=&amp;quot;${marker.iconUrl}&amp;quot; style=&amp;quot;width:${iconSize}px; height:${iconSize}px; object-fit:contain;&amp;quot;&amp;gt;`;&lt;br /&gt;
            } else {&lt;br /&gt;
                iconHtml = `&amp;lt;i class=&amp;quot;fas fa-map-marker-alt&amp;quot; style=&amp;quot;font-size:${iconSize}px;&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;`;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            $marker.innerHTML = `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-icon&amp;quot; style=&amp;quot;display:flex; align-items:center; justify-content:center; pointer-events:none;&amp;quot;&amp;gt;${iconHtml}&amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;mapa-marker-tooltip&amp;quot; style=&amp;quot;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;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;strong&amp;gt;${marker.name || 'Marcador'}&amp;lt;/strong&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseenter', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1.1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '1';&lt;br /&gt;
                    tooltip.style.visibility = 'visible';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            $marker.addEventListener('mouseleave', () =&amp;gt; {&lt;br /&gt;
                $marker.style.transform = 'translate(-50%, -50%) scale(1)';&lt;br /&gt;
                const tooltip = $marker.querySelector('.mapa-marker-tooltip');&lt;br /&gt;
                if (tooltip) {&lt;br /&gt;
                    tooltip.style.opacity = '0';&lt;br /&gt;
                    tooltip.style.visibility = 'hidden';&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            if (hasAction) {&lt;br /&gt;
                $marker.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    e.stopPropagation();&lt;br /&gt;
                    this.executeAction(marker);&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            return $marker;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        executeAction(marker) {&lt;br /&gt;
            switch (marker.action) {&lt;br /&gt;
                case 'popup':&lt;br /&gt;
                    this.showPopup(marker);&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'nextFloor':&lt;br /&gt;
                    this.nextFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'prevFloor':&lt;br /&gt;
                    this.prevFloor();&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'gotoFloor':&lt;br /&gt;
                    if (marker.actionData?.floorId !== undefined) {&lt;br /&gt;
                        this.goToFloor(marker.actionData.floorId);&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                case 'link':&lt;br /&gt;
                    if (marker.actionData?.url) {&lt;br /&gt;
                        window.open(marker.actionData.url, marker.actionData.target || '_blank');&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showPopup(marker) {&lt;br /&gt;
            let modal = document.getElementById('mapa-viewer-modal');&lt;br /&gt;
            if (!modal) {&lt;br /&gt;
                modal = document.createElement('div');&lt;br /&gt;
                modal.id = 'mapa-viewer-modal';&lt;br /&gt;
                modal.style.cssText = 'display:none; position:fixed; z-index:10000; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.7); justify-content:center; align-items:center;';&lt;br /&gt;
                modal.innerHTML = `&lt;br /&gt;
                    &amp;lt;div style=&amp;quot;background:#1e293b; border-radius:16px; max-width:350px; width:90%; padding:20px; position:relative; border:1px solid #334155;&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;span style=&amp;quot;position:absolute; right:15px; top:10px; font-size:24px; cursor:pointer; color:#94a3b8;&amp;quot;&amp;gt;&amp;amp;times;&amp;lt;/span&amp;gt;&lt;br /&gt;
                        &amp;lt;div id=&amp;quot;mapa-viewer-popup-content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                `;&lt;br /&gt;
                document.body.appendChild(modal);&lt;br /&gt;
                modal.querySelector('span').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
                    modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
                modal.addEventListener('click', (e) =&amp;gt; {&lt;br /&gt;
                    if (e.target === modal) modal.style.display = 'none';&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const content = modal.querySelector('#mapa-viewer-popup-content');&lt;br /&gt;
            content.innerHTML = `&lt;br /&gt;
                &amp;lt;div style=&amp;quot;display:flex; align-items:center; margin-bottom:12px;&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;h3 style=&amp;quot;margin:0; color:#a5b4fc;&amp;quot;&amp;gt;${marker.name}&amp;lt;/h3&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;p style=&amp;quot;color:#cbd5e1;&amp;quot;&amp;gt;${marker.actionData?.text || 'Sem informações'}&amp;lt;/p&amp;gt;&lt;br /&gt;
            `;&lt;br /&gt;
            modal.style.display = 'flex';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        updateMarkersPosition() {&lt;br /&gt;
            this.markers.forEach(({ element, data }) =&amp;gt; {&lt;br /&gt;
                if (parseInt(element.getAttribute('data-floor')) === this.currentFloor) {&lt;br /&gt;
                    const zoomedX = data.x * this.currentZoom;&lt;br /&gt;
                    const zoomedY = data.y * this.currentZoom;&lt;br /&gt;
                    element.style.left = `${zoomedX}px`;&lt;br /&gt;
                    element.style.top = `${zoomedY}px`;&lt;br /&gt;
                    &lt;br /&gt;
                    const iconSize = Math.max(24, Math.floor(24 * this.currentZoom));&lt;br /&gt;
                    const iconDiv = element.querySelector('.mapa-marker-icon');&lt;br /&gt;
                    if (iconDiv) {&lt;br /&gt;
                        if (data.iconUrl &amp;amp;&amp;amp; data.iconUrl !== '') {&lt;br /&gt;
                            let img = iconDiv.querySelector('img');&lt;br /&gt;
                            if (img) {&lt;br /&gt;
                                img.style.width = `${iconSize}px`;&lt;br /&gt;
                                img.style.height = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        } else {&lt;br /&gt;
                            let icon = iconDiv.querySelector('i');&lt;br /&gt;
                            if (icon) {&lt;br /&gt;
                                icon.style.fontSize = `${iconSize}px`;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        centerMap() {&lt;br /&gt;
            setTimeout(() =&amp;gt; {&lt;br /&gt;
                const layersWidth = this.layersContainer.scrollWidth;&lt;br /&gt;
                const layersHeight = this.layersContainer.scrollHeight;&lt;br /&gt;
                const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
                const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
                if (layersWidth &amp;gt; 0 &amp;amp;&amp;amp; layersHeight &amp;gt; 0) {&lt;br /&gt;
                    this.viewport.scrollLeft = (layersWidth - viewportWidth) / 2;&lt;br /&gt;
                    this.viewport.scrollTop = (layersHeight - viewportHeight) / 2;&lt;br /&gt;
                }&lt;br /&gt;
            }, 100);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomIn() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.min(this.currentZoom + this.zoomStep, this.maxZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        zoomOut() {&lt;br /&gt;
            const oldZoom = this.currentZoom;&lt;br /&gt;
            this.currentZoom = Math.max(this.currentZoom - this.zoomStep, this.minZoom);&lt;br /&gt;
            if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                this.applyZoomChange(oldZoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        applyZoomChange(oldZoom) {&lt;br /&gt;
            const rect = this.viewport.getBoundingClientRect();&lt;br /&gt;
            const centerX = rect.width / 2;&lt;br /&gt;
            const centerY = rect.height / 2;&lt;br /&gt;
            const scrollX = (this.viewport.scrollLeft + centerX) / oldZoom;&lt;br /&gt;
            const scrollY = (this.viewport.scrollTop + centerY) / oldZoom;&lt;br /&gt;
            &lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            &lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
            this.viewport.scrollLeft = scrollX * this.currentZoom - centerX;&lt;br /&gt;
            this.viewport.scrollTop = scrollY * this.currentZoom - centerY;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        resetView() {&lt;br /&gt;
            this.currentZoom = this.config.mapConfig?.defaultZoom || 1;&lt;br /&gt;
            document.querySelectorAll('.mapa-image').forEach(img =&amp;gt; {&lt;br /&gt;
                img.style.transform = `scale(${this.currentZoom})`;&lt;br /&gt;
            });&lt;br /&gt;
            this.centerMap();&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        prevFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;gt; 0) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex - 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        nextFloor() {&lt;br /&gt;
            const layers = this.config.layers.sort((a,b) =&amp;gt; a.id - b.id);&lt;br /&gt;
            const currentIndex = layers.findIndex(l =&amp;gt; l.id === this.currentFloor);&lt;br /&gt;
            if (currentIndex &amp;lt; layers.length - 1) {&lt;br /&gt;
                this.goToFloor(layers[currentIndex + 1].id);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToFloor(floorId) {&lt;br /&gt;
            this.currentFloor = floorId;&lt;br /&gt;
            this.layers.forEach(layer =&amp;gt; {&lt;br /&gt;
                const layerFloor = parseInt(layer.getAttribute('data-floor'));&lt;br /&gt;
                layer.style.display = layerFloor === floorId ? 'block' : 'none';&lt;br /&gt;
            });&lt;br /&gt;
            this.updateMarkersPosition();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        goToCoordinates(x, y, zoom = null, floorId = null) {&lt;br /&gt;
            if (floorId !== null &amp;amp;&amp;amp; floorId !== this.currentFloor) {&lt;br /&gt;
                this.goToFloor(floorId);&lt;br /&gt;
                setTimeout(() =&amp;gt; this._centerOnPoint(x, y, zoom), 100);&lt;br /&gt;
            } else {&lt;br /&gt;
                this._centerOnPoint(x, y, zoom);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        _centerOnPoint(x, y, zoom) {&lt;br /&gt;
            if (zoom !== null &amp;amp;&amp;amp; zoom !== this.currentZoom) {&lt;br /&gt;
                const oldZoom = this.currentZoom;&lt;br /&gt;
                this.currentZoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);&lt;br /&gt;
                if (oldZoom !== this.currentZoom) {&lt;br /&gt;
                    this.applyZoomChange(oldZoom);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            const viewportWidth = this.viewport.clientWidth;&lt;br /&gt;
            const viewportHeight = this.viewport.clientHeight;&lt;br /&gt;
            let screenX = x * this.currentZoom;&lt;br /&gt;
            let screenY = y * this.currentZoom;&lt;br /&gt;
            let targetScrollX = screenX - (viewportWidth / 2);&lt;br /&gt;
            let targetScrollY = screenY - (viewportHeight / 2);&lt;br /&gt;
            &lt;br /&gt;
            this.viewport.scrollLeft = Math.max(0, targetScrollX);&lt;br /&gt;
            this.viewport.scrollTop = Math.max(0, targetScrollY);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        setupKeyboardShortcuts() {&lt;br /&gt;
            const handler = (e) =&amp;gt; {&lt;br /&gt;
                if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;&lt;br /&gt;
                switch (e.key) {&lt;br /&gt;
                    case '+': case '=': e.preventDefault(); this.zoomIn(); break;&lt;br /&gt;
                    case '-': case '_': e.preventDefault(); this.zoomOut(); break;&lt;br /&gt;
                    case 'r': case 'R': e.preventDefault(); this.resetView(); break;&lt;br /&gt;
                    case 'ArrowUp': case 'PageUp': e.preventDefault(); this.prevFloor(); break;&lt;br /&gt;
                    case 'ArrowDown': case 'PageDown': e.preventDefault(); this.nextFloor(); break;&lt;br /&gt;
                }&lt;br /&gt;
            };&lt;br /&gt;
            document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            this.keyHandler = handler;&lt;br /&gt;
            document.addEventListener('keydown', this.keyHandler);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showError(message) {&lt;br /&gt;
            this.container.innerHTML = `&amp;lt;div style=&amp;quot;display:flex; align-items:center; justify-content:center; height:100%; color:#ef4444; text-align:center; padding:20px;&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;div&amp;gt;❌ ${message}&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;/div&amp;gt;`;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        destroy() {&lt;br /&gt;
            if (this.keyHandler) {&lt;br /&gt;
                document.removeEventListener('keydown', this.keyHandler);&lt;br /&gt;
            }&lt;br /&gt;
            this.container.innerHTML = '';&lt;br /&gt;
            this.isReady = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
(function() {&lt;br /&gt;
    var containerId = 'mapa-viewer-&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    var configJSON = &amp;lt;!--{$configJSON|default:'null'}--&amp;gt;;  // Recebe o JSON&lt;br /&gt;
    var urlJSON = '&amp;lt;!--{$url|escape:'quotes'}--&amp;gt;';        // Recebe URL do JSON&lt;br /&gt;
    &lt;br /&gt;
    // Nome da variável global única para este mapa&lt;br /&gt;
    var globalVarName = 'mapaViewer_&amp;lt;!--{$id|escape:'quotes'|default:'mapa-default'}--&amp;gt;';&lt;br /&gt;
    &lt;br /&gt;
    function iniciarMapa() {&lt;br /&gt;
        var container = document.getElementById(containerId);&lt;br /&gt;
        if (!container) return;&lt;br /&gt;
        if (container.hasAttribute('data-iniciado')) return;&lt;br /&gt;
        &lt;br /&gt;
        container.setAttribute('data-iniciado', 'true');&lt;br /&gt;
        &lt;br /&gt;
        var viewer = null;&lt;br /&gt;
        &lt;br /&gt;
        if (configJSON &amp;amp;&amp;amp; configJSON !== 'null') {&lt;br /&gt;
            // Configuração inline&lt;br /&gt;
            viewer = new MapaViewer(containerId, configJSON);&lt;br /&gt;
        } else if (urlJSON) {&lt;br /&gt;
            // Configuração via URL&lt;br /&gt;
            fetch(urlJSON)&lt;br /&gt;
                .then(response =&amp;gt; response.json())&lt;br /&gt;
                .then(config =&amp;gt; {&lt;br /&gt;
                    window[globalVarName] = new MapaViewer(containerId, config);&lt;br /&gt;
                })&lt;br /&gt;
                .catch(error =&amp;gt; {&lt;br /&gt;
                    console.error('Erro ao carregar mapa:', error);&lt;br /&gt;
                    container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Erro ao carregar mapa: ' + error.message + '&amp;lt;/div&amp;gt;';&lt;br /&gt;
                });&lt;br /&gt;
            return; // Sai porque o fetch é assíncrono&lt;br /&gt;
        } else {&lt;br /&gt;
            container.innerHTML = '&amp;lt;div style=&amp;quot;color:red;padding:20px;&amp;quot;&amp;gt;❌ Nenhuma configuração fornecida&amp;lt;/div&amp;gt;';&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Guarda a instância globalmente para acesso externo&lt;br /&gt;
        if (viewer) {&lt;br /&gt;
            window[globalVarName] = viewer;&lt;br /&gt;
            console.log('Mapa inicializado! Use window.' + globalVarName + ' para controlar');&lt;br /&gt;
            console.log('Exemplo: window.' + globalVarName + '.nextFloor()');&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (document.readyState === 'loading') {&lt;br /&gt;
        document.addEventListener('DOMContentLoaded', iniciarMapa);&lt;br /&gt;
    } else {&lt;br /&gt;
        iniciarMapa();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Mapa:BlackMarket&amp;diff=45989</id>
		<title>Mapa:BlackMarket</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Mapa:BlackMarket&amp;diff=45989"/>
		<updated>2026-04-11T13:21:51Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
  &amp;quot;mapConfig&amp;quot;: {&lt;br /&gt;
    &amp;quot;initialFloor&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;defaultZoom&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;minZoom&amp;quot;: 0.3,&lt;br /&gt;
    &amp;quot;maxZoom&amp;quot;: 3,&lt;br /&gt;
    &amp;quot;zoomStep&amp;quot;: 0.1&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;layers&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Andar&amp;quot;,&lt;br /&gt;
      &amp;quot;imageUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/b/b7/Black_market_andar01.png&amp;quot;,&lt;br /&gt;
      &amp;quot;offsetX&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;offsetY&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;scale&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;opacity&amp;quot;: 100,&lt;br /&gt;
      &amp;quot;markers&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913455257&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 641.25,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 385,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913456593&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 641.25,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 257.5,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Andar&amp;quot;,&lt;br /&gt;
      &amp;quot;imageUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/2/22/Black_market_terreo.png&amp;quot;,&lt;br /&gt;
      &amp;quot;offsetX&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;offsetY&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;scale&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;opacity&amp;quot;: 100,&lt;br /&gt;
      &amp;quot;markers&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913428226&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 245,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 490,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913431714&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 580,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 538,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913440529&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 693.076923076923,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 489.9999999999999,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913443737&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 804.6153846153844,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 539.2307692307692,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913449649&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 1157.2727272727273,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 506.3636363636363,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;nextFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913482097&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 638.8888888888889,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 385.55555555555554,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913483337&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 641.1111111111111,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 255.55555555555554,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 2,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Andar&amp;quot;,&lt;br /&gt;
      &amp;quot;imageUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/d/d8/Black_market_subsolo01.png&amp;quot;,&lt;br /&gt;
      &amp;quot;offsetX&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;offsetY&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;scale&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;opacity&amp;quot;: 100,&lt;br /&gt;
      &amp;quot;markers&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913462330&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 693.75,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 508.75,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913463385&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 711.25,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 511.25,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913466441&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 725,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 510,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913469345&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 1172.5,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 510,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;m_1775913474121&amp;quot;,&lt;br /&gt;
          &amp;quot;name&amp;quot;: &amp;quot;Novo Marcador&amp;quot;,&lt;br /&gt;
          &amp;quot;x&amp;quot;: 245.55555555555554,&lt;br /&gt;
          &amp;quot;y&amp;quot;: 496.66666666666663,&lt;br /&gt;
          &amp;quot;iconUrl&amp;quot;: &amp;quot;https://wiki.gla.com.br/images/a/ab/Escada.png&amp;quot;,&lt;br /&gt;
          &amp;quot;hasBadge&amp;quot;: false,&lt;br /&gt;
          &amp;quot;action&amp;quot;: &amp;quot;prevFloor&amp;quot;,&lt;br /&gt;
          &amp;quot;actionData&amp;quot;: {}&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Escada.png&amp;diff=45988</id>
		<title>Arquivo:Escada.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Escada.png&amp;diff=45988"/>
		<updated>2026-04-11T13:15:40Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Black_market_terreo.png&amp;diff=45987</id>
		<title>Arquivo:Black market terreo.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Black_market_terreo.png&amp;diff=45987"/>
		<updated>2026-04-11T13:10:02Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Black_market_subsolo01.png&amp;diff=45986</id>
		<title>Arquivo:Black market subsolo01.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Black_market_subsolo01.png&amp;diff=45986"/>
		<updated>2026-04-11T13:10:01Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Black_market_andar01.png&amp;diff=45985</id>
		<title>Arquivo:Black market andar01.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Black_market_andar01.png&amp;diff=45985"/>
		<updated>2026-04-11T13:09:59Z</updated>

		<summary type="html">&lt;p&gt;Ceu azul: Arquivo enviado com MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Arquivo enviado com MsUpload&lt;/div&gt;</summary>
		<author><name>Ceu azul</name></author>
	</entry>
</feed>