<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mewsie.world/CoraTOWiki/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AMindMap.js</id>
	<title>MediaWiki:MindMap.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mewsie.world/CoraTOWiki/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AMindMap.js"/>
	<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MindMap.js&amp;action=history"/>
	<updated>2026-05-04T17:34:26Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MindMap.js&amp;diff=6607&amp;oldid=prev</id>
		<title>Noorisei at 09:22, 30 December 2025</title>
		<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MindMap.js&amp;diff=6607&amp;oldid=prev"/>
		<updated>2025-12-30T09:22:17Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;a href=&quot;https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MindMap.js&amp;amp;diff=6607&amp;amp;oldid=6441&quot;&gt;Show changes&lt;/a&gt;</summary>
		<author><name>Noorisei</name></author>
	</entry>
	<entry>
		<id>https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MindMap.js&amp;diff=6441&amp;oldid=prev</id>
		<title>Noorisei: Created page with &quot;(function(){       function initMap(container){         var stage=container.querySelector(&#039;.mindmap-stage&#039;); if(!stage)return;         var links=container.querySelector(&#039;.mm-links&#039;); var detail=container.querySelector(&#039;.mm-detail&#039;);         var scale=1, tx=0, ty=0, panning=false, px=0, py=0;         var rafDraw=0;         function clamp(n,min,max){ return Math.min(max,Math.max(min,n)); }         function scheduleDraw(){           if(rafDraw) return;           rafDraw=win...&quot;</title>
		<link rel="alternate" type="text/html" href="https://mewsie.world/CoraTOWiki/index.php?title=MediaWiki:MindMap.js&amp;diff=6441&amp;oldid=prev"/>
		<updated>2025-12-17T06:35:43Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;(function(){       function initMap(container){         var stage=container.querySelector(&amp;#039;.mindmap-stage&amp;#039;); if(!stage)return;         var links=container.querySelector(&amp;#039;.mm-links&amp;#039;); var detail=container.querySelector(&amp;#039;.mm-detail&amp;#039;);         var scale=1, tx=0, ty=0, panning=false, px=0, py=0;         var rafDraw=0;         function clamp(n,min,max){ return Math.min(max,Math.max(min,n)); }         function scheduleDraw(){           if(rafDraw) return;           rafDraw=win...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;(function(){&lt;br /&gt;
      function initMap(container){&lt;br /&gt;
        var stage=container.querySelector(&amp;#039;.mindmap-stage&amp;#039;); if(!stage)return;&lt;br /&gt;
        var links=container.querySelector(&amp;#039;.mm-links&amp;#039;); var detail=container.querySelector(&amp;#039;.mm-detail&amp;#039;);&lt;br /&gt;
        var scale=1, tx=0, ty=0, panning=false, px=0, py=0;&lt;br /&gt;
        var rafDraw=0;&lt;br /&gt;
        function clamp(n,min,max){ return Math.min(max,Math.max(min,n)); }&lt;br /&gt;
        function scheduleDraw(){&lt;br /&gt;
          if(rafDraw) return;&lt;br /&gt;
          rafDraw=window.requestAnimationFrame(function(){ rafDraw=0; drawLines(); });&lt;br /&gt;
        }&lt;br /&gt;
        function applyTransform(){ stage.style.transform=&amp;#039;translate(&amp;#039;+tx+&amp;#039;px,&amp;#039;+ty+&amp;#039;px) scale(&amp;#039;+scale+&amp;#039;)&amp;#039;; scheduleDraw(); }&lt;br /&gt;
        function layoutBranches(){&lt;br /&gt;
          if(container.clientWidth===0 || container.clientHeight===0) return;&lt;br /&gt;
          var pad=24;&lt;br /&gt;
          var gapX=140;&lt;br /&gt;
          var gapY=34;&lt;br /&gt;
          var center=stage.querySelector(&amp;#039;.mm-center&amp;#039;);&lt;br /&gt;
          if(center){&lt;br /&gt;
            center.style.left=pad+&amp;#039;px&amp;#039;;&lt;br /&gt;
            center.style.top=(container.clientHeight/2)+&amp;#039;px&amp;#039;;&lt;br /&gt;
            center.style.transform=&amp;#039;translate(0, -50%)&amp;#039;;&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
          var cw=center ? center.offsetWidth : 220;&lt;br /&gt;
          var cx=pad + cw + gapX;&lt;br /&gt;
          var y=pad;&lt;br /&gt;
&lt;br /&gt;
          var branches=stage.querySelectorAll(&amp;#039;.mm-branch&amp;#039;);&lt;br /&gt;
          branches.forEach(function(b){&lt;br /&gt;
            b.style.left=cx+&amp;#039;px&amp;#039;;&lt;br /&gt;
            b.style.top=y+&amp;#039;px&amp;#039;;&lt;br /&gt;
            y+=b.offsetHeight+gapY;&lt;br /&gt;
          });&lt;br /&gt;
          scheduleDraw();&lt;br /&gt;
        }&lt;br /&gt;
        function zoomAt(clientX, clientY, nextScale){&lt;br /&gt;
          var rect=container.getBoundingClientRect();&lt;br /&gt;
          var mx=clientX-rect.left;&lt;br /&gt;
          var my=clientY-rect.top;&lt;br /&gt;
          var prev=scale;&lt;br /&gt;
          var ns=clamp(nextScale,0.6,2.2);&lt;br /&gt;
          if(ns===prev) return;&lt;br /&gt;
          var wx=(mx-tx)/prev;&lt;br /&gt;
          var wy=(my-ty)/prev;&lt;br /&gt;
          scale=ns;&lt;br /&gt;
          tx=mx-wx*scale;&lt;br /&gt;
          ty=my-wy*scale;&lt;br /&gt;
          applyTransform();&lt;br /&gt;
        }&lt;br /&gt;
        function centerOnEl(node){&lt;br /&gt;
          if(!node)return;&lt;br /&gt;
          var nr=node.getBoundingClientRect();&lt;br /&gt;
          var cr=container.getBoundingClientRect();&lt;br /&gt;
          var dx=(cr.left+cr.width/2)-(nr.left+nr.width/2);&lt;br /&gt;
          var dy=(cr.top+cr.height/2)-(nr.top+nr.height/2);&lt;br /&gt;
          tx+=dx;&lt;br /&gt;
          ty+=dy;&lt;br /&gt;
          applyTransform();&lt;br /&gt;
        }&lt;br /&gt;
        function showDetailForNode(node){&lt;br /&gt;
          if(!detail || !node) return;&lt;br /&gt;
          var d=node.getAttribute(&amp;#039;data-detail&amp;#039;);&lt;br /&gt;
          if(!d){&lt;br /&gt;
            var branch=node.parentElement &amp;amp;&amp;amp; node.parentElement.classList &amp;amp;&amp;amp; node.parentElement.classList.contains(&amp;#039;mm-branch&amp;#039;) ? node.parentElement : null;&lt;br /&gt;
            if(branch){&lt;br /&gt;
              var kids=branch.querySelectorAll(&amp;#039;:scope &amp;gt; .mm-children &amp;gt; .mm-node, :scope &amp;gt; .mm-children &amp;gt; .mm-item &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
              if(kids &amp;amp;&amp;amp; kids.length){&lt;br /&gt;
                var labels=[];&lt;br /&gt;
                kids.forEach(function(k){ labels.push((k.textContent||&amp;#039;&amp;#039;).trim()); });&lt;br /&gt;
                d=((node.textContent||&amp;#039;&amp;#039;).trim())+&amp;#039; — &amp;#039;+labels.join(&amp;#039;, &amp;#039;);&lt;br /&gt;
              }&lt;br /&gt;
            }&lt;br /&gt;
          }&lt;br /&gt;
          if(d){ detail.textContent=d; detail.classList.add(&amp;#039;active&amp;#039;); } else { detail.classList.remove(&amp;#039;active&amp;#039;); }&lt;br /&gt;
        }&lt;br /&gt;
        function drawLines(){&lt;br /&gt;
          var svgW=container.clientWidth, svgH=container.clientHeight;&lt;br /&gt;
          if(svgW===0 || svgH===0) return;&lt;br /&gt;
          links.setAttribute(&amp;#039;width&amp;#039;,svgW); links.setAttribute(&amp;#039;height&amp;#039;,svgH);&lt;br /&gt;
          links.innerHTML=&amp;#039;&amp;#039;;&lt;br /&gt;
          function addCurve(a,b){&lt;br /&gt;
            var ar=a.getBoundingClientRect(), br=b.getBoundingClientRect(), cr=container.getBoundingClientRect();&lt;br /&gt;
            var x1=ar.right-cr.left;&lt;br /&gt;
            var y1=(ar.top-cr.top)+ar.height/2;&lt;br /&gt;
            var x2=br.left-cr.left;&lt;br /&gt;
            var y2=(br.top-cr.top)+br.height/2;&lt;br /&gt;
            var dist=Math.abs(x2-x1);&lt;br /&gt;
            var dx=Math.max(40, Math.min(180, dist*0.45));&lt;br /&gt;
            var c1x=x1+dx;&lt;br /&gt;
            var c2x=x2-dx;&lt;br /&gt;
            var path=document.createElementNS(&amp;#039;http://www.w3.org/2000/svg&amp;#039;,&amp;#039;path&amp;#039;);&lt;br /&gt;
            path.setAttribute(&amp;#039;d&amp;#039;,&amp;#039;M &amp;#039;+x1+&amp;#039; &amp;#039;+y1+&amp;#039; C &amp;#039;+c1x+&amp;#039; &amp;#039;+y1+&amp;#039;, &amp;#039;+c2x+&amp;#039; &amp;#039;+y2+&amp;#039;, &amp;#039;+x2+&amp;#039; &amp;#039;+y2);&lt;br /&gt;
            path.setAttribute(&amp;#039;fill&amp;#039;,&amp;#039;none&amp;#039;);&lt;br /&gt;
            path.setAttribute(&amp;#039;stroke&amp;#039;,&amp;#039;rgba(255,107,157,.35)&amp;#039;);&lt;br /&gt;
            path.setAttribute(&amp;#039;stroke-width&amp;#039;,&amp;#039;2&amp;#039;);&lt;br /&gt;
            links.appendChild(path);&lt;br /&gt;
          }&lt;br /&gt;
          var center=stage.querySelector(&amp;#039;.mm-center&amp;#039;);&lt;br /&gt;
          if(!center) return;&lt;br /&gt;
          var branches=stage.querySelectorAll(&amp;#039;.mm-branch&amp;#039;);&lt;br /&gt;
          branches.forEach(function(b){&lt;br /&gt;
            var head=b.querySelector(&amp;#039;:scope &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
            if(!head) return;&lt;br /&gt;
            addCurve(center,head);&lt;br /&gt;
            if(b.classList.contains(&amp;#039;open&amp;#039;)){&lt;br /&gt;
              var directChildren=b.querySelectorAll(&amp;#039;:scope &amp;gt; .mm-children &amp;gt; .mm-node, :scope &amp;gt; .mm-children &amp;gt; .mm-item &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
              directChildren.forEach(function(c){ addCurve(head,c); });&lt;br /&gt;
              var openItems=b.querySelectorAll(&amp;#039;:scope &amp;gt; .mm-children &amp;gt; .mm-item.open&amp;#039;);&lt;br /&gt;
              openItems.forEach(function(item){&lt;br /&gt;
                var itemNode=item.querySelector(&amp;#039;:scope &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
                if(!itemNode) return;&lt;br /&gt;
                var subs=item.querySelectorAll(&amp;#039;:scope &amp;gt; .mm-subchildren &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
                subs.forEach(function(s){ addCurve(itemNode,s); });&lt;br /&gt;
              });&lt;br /&gt;
            }&lt;br /&gt;
          });&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var down=false, dragging=false, downId=null, startX=0, startY=0;&lt;br /&gt;
        container.addEventListener(&amp;#039;pointerdown&amp;#039;,function(e){&lt;br /&gt;
          if(e.button!==0) return;&lt;br /&gt;
          if(e.target.closest(&amp;#039;.mm-controls&amp;#039;)) return;&lt;br /&gt;
          down=true;&lt;br /&gt;
          dragging=false;&lt;br /&gt;
          downId=e.pointerId;&lt;br /&gt;
          startX=e.clientX;&lt;br /&gt;
          startY=e.clientY;&lt;br /&gt;
          px=e.clientX;&lt;br /&gt;
          py=e.clientY;&lt;br /&gt;
        });&lt;br /&gt;
        container.addEventListener(&amp;#039;pointermove&amp;#039;,function(e){&lt;br /&gt;
          if(!down || downId!==e.pointerId) return;&lt;br /&gt;
          var dx=e.clientX-px, dy=e.clientY-py;&lt;br /&gt;
          px=e.clientX;&lt;br /&gt;
          py=e.clientY;&lt;br /&gt;
          if(!dragging){&lt;br /&gt;
            var mx=e.clientX-startX, my=e.clientY-startY;&lt;br /&gt;
            if(Math.abs(mx)+Math.abs(my) &amp;lt; 6) return;&lt;br /&gt;
            dragging=true;&lt;br /&gt;
            container.classList.add(&amp;#039;mm-dragging&amp;#039;);&lt;br /&gt;
            try{ container.setPointerCapture(e.pointerId); }catch(err){}&lt;br /&gt;
          }&lt;br /&gt;
          tx+=dx;&lt;br /&gt;
          ty+=dy;&lt;br /&gt;
          applyTransform();&lt;br /&gt;
        });&lt;br /&gt;
        function endPointer(e){&lt;br /&gt;
          if(downId!==e.pointerId) return;&lt;br /&gt;
          down=false;&lt;br /&gt;
          downId=null;&lt;br /&gt;
          if(dragging){&lt;br /&gt;
            dragging=false;&lt;br /&gt;
            container.classList.remove(&amp;#039;mm-dragging&amp;#039;);&lt;br /&gt;
            try{ container.releasePointerCapture(e.pointerId); }catch(err){}&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
        container.addEventListener(&amp;#039;pointerup&amp;#039;,endPointer);&lt;br /&gt;
        container.addEventListener(&amp;#039;pointercancel&amp;#039;,endPointer);&lt;br /&gt;
&lt;br /&gt;
        container.addEventListener(&amp;#039;wheel&amp;#039;,function(e){&lt;br /&gt;
          e.preventDefault();&lt;br /&gt;
          var next=scale+(e.deltaY&amp;lt;0?0.12:-0.12);&lt;br /&gt;
          zoomAt(e.clientX,e.clientY,next);&lt;br /&gt;
        },{passive:false});&lt;br /&gt;
&lt;br /&gt;
        var btnIn=container.querySelector(&amp;#039;[data-mm-zoom=&amp;quot;in&amp;quot;]&amp;#039;);&lt;br /&gt;
        var btnOut=container.querySelector(&amp;#039;[data-mm-zoom=&amp;quot;out&amp;quot;]&amp;#039;);&lt;br /&gt;
        var btnReset=container.querySelector(&amp;#039;[data-mm-reset]&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
        function zoomAtCenter(next){&lt;br /&gt;
          var cr=container.getBoundingClientRect();&lt;br /&gt;
          zoomAt(cr.left+cr.width/2, cr.top+cr.height/2, next);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if(btnIn)btnIn.addEventListener(&amp;#039;click&amp;#039;,function(){ zoomAtCenter(scale+0.15); });&lt;br /&gt;
        if(btnOut)btnOut.addEventListener(&amp;#039;click&amp;#039;,function(){ zoomAtCenter(scale-0.15); });&lt;br /&gt;
        if(btnReset)btnReset.addEventListener(&amp;#039;click&amp;#039;,function(){ scale=1; tx=0; ty=0; applyTransform(); });&lt;br /&gt;
&lt;br /&gt;
        stage.querySelectorAll(&amp;#039;.mm-node&amp;#039;).forEach(function(n){&lt;br /&gt;
          n.addEventListener(&amp;#039;click&amp;#039;,function(){&lt;br /&gt;
            showDetailForNode(n);&lt;br /&gt;
          });&lt;br /&gt;
        });&lt;br /&gt;
        stage.querySelectorAll(&amp;#039;.mm-branch &amp;gt; .mm-node&amp;#039;).forEach(function(head){&lt;br /&gt;
          head.addEventListener(&amp;#039;click&amp;#039;,function(){&lt;br /&gt;
            var branch=head.parentElement;&lt;br /&gt;
            if(branch.classList.contains(&amp;#039;open&amp;#039;)){ branch.classList.remove(&amp;#039;open&amp;#039;); } else { branch.classList.add(&amp;#039;open&amp;#039;); }&lt;br /&gt;
            layoutBranches();&lt;br /&gt;
            scheduleDraw();&lt;br /&gt;
          });&lt;br /&gt;
        });&lt;br /&gt;
        stage.addEventListener(&amp;#039;click&amp;#039;,function(e){&lt;br /&gt;
          var node=e.target.closest(&amp;#039;.mm-item &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
          if(!node) return;&lt;br /&gt;
          var item=node.parentElement;&lt;br /&gt;
          if(!item) return;&lt;br /&gt;
          if(item.classList.contains(&amp;#039;open&amp;#039;)){ item.classList.remove(&amp;#039;open&amp;#039;); } else { item.classList.add(&amp;#039;open&amp;#039;); }&lt;br /&gt;
          layoutBranches();&lt;br /&gt;
          scheduleDraw();&lt;br /&gt;
        });&lt;br /&gt;
        window.addEventListener(&amp;#039;resize&amp;#039;, function(){ layoutBranches(); drawLines(); });&lt;br /&gt;
        layoutBranches();&lt;br /&gt;
        applyTransform();&lt;br /&gt;
&lt;br /&gt;
        container.__mmApi={&lt;br /&gt;
          layout: layoutBranches,&lt;br /&gt;
          draw: drawLines,&lt;br /&gt;
          showDetail: showDetailForNode&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      function decorateLinkNodes(root){&lt;br /&gt;
        (root||document).querySelectorAll(&amp;#039;.mm-node[data-href]&amp;#039;).forEach(function(node){&lt;br /&gt;
          if(node.querySelector(&amp;#039;a&amp;#039;)) return;&lt;br /&gt;
          var href=node.getAttribute(&amp;#039;data-href&amp;#039;);&lt;br /&gt;
          if(!href) return;&lt;br /&gt;
          var label=(node.textContent||&amp;#039;&amp;#039;).trim();&lt;br /&gt;
          node.textContent=&amp;#039;&amp;#039;;&lt;br /&gt;
          var a=document.createElement(&amp;#039;a&amp;#039;);&lt;br /&gt;
          a.href=href;&lt;br /&gt;
          a.target=&amp;#039;_blank&amp;#039;;&lt;br /&gt;
          a.rel=&amp;#039;noopener&amp;#039;;&lt;br /&gt;
          a.textContent=label;&lt;br /&gt;
          node.appendChild(a);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        document.addEventListener(&amp;#039;click&amp;#039;, function(e){&lt;br /&gt;
          var a=e.target.closest(&amp;#039;.mm-node a&amp;#039;);&lt;br /&gt;
          if(!a) return;&lt;br /&gt;
          e.stopPropagation();&lt;br /&gt;
        }, true);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      function renderLocationsTables(){&lt;br /&gt;
        var root=document.getElementById(&amp;#039;locations-table-root&amp;#039;);&lt;br /&gt;
        var stage=document.getElementById(&amp;#039;mm-stage-locations&amp;#039;);&lt;br /&gt;
        if(!root || !stage) return;&lt;br /&gt;
&lt;br /&gt;
        var frag=document.createDocumentFragment();&lt;br /&gt;
        var branches=stage.querySelectorAll(&amp;#039;.mm-branch&amp;#039;);&lt;br /&gt;
        branches.forEach(function(branch){&lt;br /&gt;
          var branchHead=branch.querySelector(&amp;#039;:scope &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
          var branchTitle=(branchHead &amp;amp;&amp;amp; branchHead.textContent ? branchHead.textContent : &amp;#039;&amp;#039;).trim();&lt;br /&gt;
          var theme=branch.id ? branch.id.replace(/^mm-locations-/, &amp;#039;&amp;#039;) : &amp;#039;default&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
          var card=document.createElement(&amp;#039;section&amp;#039;);&lt;br /&gt;
          card.className=&amp;#039;mm-table-card mm-table-card--&amp;#039;+theme;&lt;br /&gt;
&lt;br /&gt;
          var h=document.createElement(&amp;#039;h3&amp;#039;);&lt;br /&gt;
          h.className=&amp;#039;mm-table-title&amp;#039;;&lt;br /&gt;
          h.textContent=branchTitle || &amp;#039;Locations&amp;#039;;&lt;br /&gt;
          card.appendChild(h);&lt;br /&gt;
&lt;br /&gt;
          var table=document.createElement(&amp;#039;table&amp;#039;);&lt;br /&gt;
          table.className=&amp;#039;mm-table&amp;#039;;&lt;br /&gt;
          table.setAttribute(&amp;#039;data-theme&amp;#039;, theme);&lt;br /&gt;
&lt;br /&gt;
          var thead=document.createElement(&amp;#039;thead&amp;#039;);&lt;br /&gt;
          thead.innerHTML=&amp;#039;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Town&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Location(s)&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;#039;;&lt;br /&gt;
          table.appendChild(thead);&lt;br /&gt;
&lt;br /&gt;
          var tbody=document.createElement(&amp;#039;tbody&amp;#039;);&lt;br /&gt;
          var items=branch.querySelectorAll(&amp;#039;:scope &amp;gt; .mm-children &amp;gt; .mm-item&amp;#039;);&lt;br /&gt;
          items.forEach(function(item){&lt;br /&gt;
            var townNode=item.querySelector(&amp;#039;:scope &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
            var town=(townNode &amp;amp;&amp;amp; townNode.textContent ? townNode.textContent : &amp;#039;&amp;#039;).trim();&lt;br /&gt;
            if(!town) return;&lt;br /&gt;
&lt;br /&gt;
            var tr=document.createElement(&amp;#039;tr&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
            var tdTown=document.createElement(&amp;#039;td&amp;#039;);&lt;br /&gt;
            tdTown.className=&amp;#039;mm-td-town&amp;#039;;&lt;br /&gt;
            tdTown.textContent=town;&lt;br /&gt;
            tr.appendChild(tdTown);&lt;br /&gt;
&lt;br /&gt;
            var tdLoc=document.createElement(&amp;#039;td&amp;#039;);&lt;br /&gt;
            tdLoc.className=&amp;#039;mm-td-locations&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
            var subs=item.querySelectorAll(&amp;#039;:scope &amp;gt; .mm-subchildren &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
            if(subs &amp;amp;&amp;amp; subs.length){&lt;br /&gt;
              var ul=document.createElement(&amp;#039;ul&amp;#039;);&lt;br /&gt;
              ul.className=&amp;#039;mm-list&amp;#039;;&lt;br /&gt;
              subs.forEach(function(s){&lt;br /&gt;
                var li=document.createElement(&amp;#039;li&amp;#039;);&lt;br /&gt;
                li.textContent=((s.textContent||&amp;#039;&amp;#039;).trim());&lt;br /&gt;
                ul.appendChild(li);&lt;br /&gt;
              });&lt;br /&gt;
              tdLoc.appendChild(ul);&lt;br /&gt;
            }else{&lt;br /&gt;
              tdLoc.textContent=&amp;#039;—&amp;#039;;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            tr.appendChild(tdLoc);&lt;br /&gt;
            tbody.appendChild(tr);&lt;br /&gt;
          });&lt;br /&gt;
&lt;br /&gt;
          table.appendChild(tbody);&lt;br /&gt;
          card.appendChild(table);&lt;br /&gt;
          frag.appendChild(card);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        root.textContent=&amp;#039;&amp;#039;;&lt;br /&gt;
        root.appendChild(frag);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      function parseDetailTriplet(detail){&lt;br /&gt;
        var raw=(detail||&amp;#039;&amp;#039;).trim();&lt;br /&gt;
        if(!raw) return { left:&amp;#039;&amp;#039;, mid:&amp;#039;&amp;#039;, right:&amp;#039;&amp;#039; };&lt;br /&gt;
        var parts=raw.split(/\s*—\s*/g).map(function(s){ return s.trim(); }).filter(Boolean);&lt;br /&gt;
        return {&lt;br /&gt;
          left: parts[0] || &amp;#039;&amp;#039;,&lt;br /&gt;
          mid: parts[1] || &amp;#039;&amp;#039;,&lt;br /&gt;
          right: parts.slice(2).join(&amp;#039; — &amp;#039;)&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      function initServicesTable(){&lt;br /&gt;
        var root=document.getElementById(&amp;#039;services-table-root&amp;#039;);&lt;br /&gt;
        var stage=document.getElementById(&amp;#039;mm-stage-services&amp;#039;);&lt;br /&gt;
        if(!root || !stage) return;&lt;br /&gt;
&lt;br /&gt;
        var rows=[];&lt;br /&gt;
        stage.querySelectorAll(&amp;#039;.mm-branch&amp;#039;).forEach(function(branch){&lt;br /&gt;
          var head=branch.querySelector(&amp;#039;:scope &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
          var category=((head &amp;amp;&amp;amp; head.textContent)||&amp;#039;&amp;#039;).trim();&lt;br /&gt;
          var children=branch.querySelectorAll(&amp;#039;:scope &amp;gt; .mm-children &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
          children.forEach(function(n){&lt;br /&gt;
            var name=((n.textContent)||&amp;#039;&amp;#039;).trim();&lt;br /&gt;
            var href=n.getAttribute(&amp;#039;data-href&amp;#039;) || &amp;#039;&amp;#039;;&lt;br /&gt;
            var d=parseDetailTriplet(n.getAttribute(&amp;#039;data-detail&amp;#039;)||&amp;#039;&amp;#039;);&lt;br /&gt;
            rows.push({&lt;br /&gt;
              category: category,&lt;br /&gt;
              name: name,&lt;br /&gt;
              npc: d.mid,&lt;br /&gt;
              purpose: d.right,&lt;br /&gt;
              href: href,&lt;br /&gt;
              detail: (n.getAttribute(&amp;#039;data-detail&amp;#039;)||&amp;#039;&amp;#039;).trim()&lt;br /&gt;
            });&lt;br /&gt;
          });&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        root.innerHTML=&amp;#039;&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        var controls=document.createElement(&amp;#039;div&amp;#039;);&lt;br /&gt;
        controls.className=&amp;#039;mm-table-controls&amp;#039;;&lt;br /&gt;
        controls.innerHTML=&amp;#039;\&lt;br /&gt;
          &amp;lt;div class=&amp;quot;mm-table-control&amp;quot;&amp;gt;\&lt;br /&gt;
            &amp;lt;label class=&amp;quot;mm-table-label&amp;quot; for=&amp;quot;services-table-search&amp;quot;&amp;gt;Search&amp;lt;/label&amp;gt;\&lt;br /&gt;
            &amp;lt;input id=&amp;quot;services-table-search&amp;quot; class=&amp;quot;mm-table-input&amp;quot; type=&amp;quot;search&amp;quot; placeholder=&amp;quot;Type to filter…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;\&lt;br /&gt;
          &amp;lt;/div&amp;gt;\&lt;br /&gt;
          &amp;lt;div class=&amp;quot;mm-table-control&amp;quot;&amp;gt;\&lt;br /&gt;
            &amp;lt;label class=&amp;quot;mm-table-label&amp;quot; for=&amp;quot;services-table-category&amp;quot;&amp;gt;Category&amp;lt;/label&amp;gt;\&lt;br /&gt;
            &amp;lt;select id=&amp;quot;services-table-category&amp;quot; class=&amp;quot;mm-table-select&amp;quot;&amp;gt;\&lt;br /&gt;
              &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;All&amp;lt;/option&amp;gt;\&lt;br /&gt;
            &amp;lt;/select&amp;gt;\&lt;br /&gt;
          &amp;lt;/div&amp;gt;\&lt;br /&gt;
          &amp;lt;div class=&amp;quot;mm-table-control mm-table-meta&amp;quot; id=&amp;quot;services-table-meta&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;#039;;&lt;br /&gt;
        root.appendChild(controls);&lt;br /&gt;
&lt;br /&gt;
        var tableWrap=document.createElement(&amp;#039;div&amp;#039;);&lt;br /&gt;
        tableWrap.className=&amp;#039;mm-table-wrap&amp;#039;;&lt;br /&gt;
        root.appendChild(tableWrap);&lt;br /&gt;
&lt;br /&gt;
        var table=document.createElement(&amp;#039;table&amp;#039;);&lt;br /&gt;
        table.className=&amp;#039;mm-table mm-table--enhanced&amp;#039;;&lt;br /&gt;
        table.innerHTML=&amp;#039;\&lt;br /&gt;
          &amp;lt;thead&amp;gt;\&lt;br /&gt;
            &amp;lt;tr&amp;gt;\&lt;br /&gt;
              &amp;lt;th scope=&amp;quot;col&amp;quot; data-key=&amp;quot;category&amp;quot;&amp;gt;Category&amp;lt;/th&amp;gt;\&lt;br /&gt;
              &amp;lt;th scope=&amp;quot;col&amp;quot; data-key=&amp;quot;name&amp;quot;&amp;gt;Function&amp;lt;/th&amp;gt;\&lt;br /&gt;
              &amp;lt;th scope=&amp;quot;col&amp;quot; data-key=&amp;quot;npc&amp;quot;&amp;gt;Replaced NPC&amp;lt;/th&amp;gt;\&lt;br /&gt;
              &amp;lt;th scope=&amp;quot;col&amp;quot; data-key=&amp;quot;purpose&amp;quot;&amp;gt;What\&amp;#039;s It For&amp;lt;/th&amp;gt;\&lt;br /&gt;
              &amp;lt;th scope=&amp;quot;col&amp;quot; data-key=&amp;quot;link&amp;quot;&amp;gt;Link&amp;lt;/th&amp;gt;\&lt;br /&gt;
            &amp;lt;/tr&amp;gt;\&lt;br /&gt;
          &amp;lt;/thead&amp;gt;\&lt;br /&gt;
          &amp;lt;tbody&amp;gt;&amp;lt;/tbody&amp;gt;&amp;#039;;&lt;br /&gt;
        tableWrap.appendChild(table);&lt;br /&gt;
&lt;br /&gt;
        var tbody=table.querySelector(&amp;#039;tbody&amp;#039;);&lt;br /&gt;
        var searchEl=root.querySelector(&amp;#039;#services-table-search&amp;#039;);&lt;br /&gt;
        var categoryEl=root.querySelector(&amp;#039;#services-table-category&amp;#039;);&lt;br /&gt;
        var metaEl=root.querySelector(&amp;#039;#services-table-meta&amp;#039;);&lt;br /&gt;
        var sortKey=&amp;#039;category&amp;#039;;&lt;br /&gt;
        var sortDir=&amp;#039;asc&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        function normalize(s){ return (s||&amp;#039;&amp;#039;).toLowerCase(); }&lt;br /&gt;
        function compare(a,b){&lt;br /&gt;
          var av=normalize(a), bv=normalize(b);&lt;br /&gt;
          if(av&amp;lt;bv) return -1;&lt;br /&gt;
          if(av&amp;gt;bv) return 1;&lt;br /&gt;
          return 0;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function apply(){&lt;br /&gt;
          var q=normalize(searchEl &amp;amp;&amp;amp; searchEl.value);&lt;br /&gt;
          var cat=(categoryEl &amp;amp;&amp;amp; categoryEl.value) || &amp;#039;&amp;#039;;&lt;br /&gt;
          var filtered=rows.filter(function(r){&lt;br /&gt;
            if(cat &amp;amp;&amp;amp; r.category!==cat) return false;&lt;br /&gt;
            if(!q) return true;&lt;br /&gt;
            var blob=(r.category+&amp;#039; &amp;#039;+r.name+&amp;#039; &amp;#039;+r.npc+&amp;#039; &amp;#039;+r.purpose+&amp;#039; &amp;#039;+r.detail).toLowerCase();&lt;br /&gt;
            return blob.indexOf(q)!==-1;&lt;br /&gt;
          });&lt;br /&gt;
&lt;br /&gt;
          filtered.sort(function(a,b){&lt;br /&gt;
            var ka=(sortKey===&amp;#039;link&amp;#039;? (a.href? a.name : &amp;#039;&amp;#039;) : a[sortKey]) || &amp;#039;&amp;#039;;&lt;br /&gt;
            var kb=(sortKey===&amp;#039;link&amp;#039;? (b.href? b.name : &amp;#039;&amp;#039;) : b[sortKey]) || &amp;#039;&amp;#039;;&lt;br /&gt;
            var c=compare(ka,kb);&lt;br /&gt;
            return sortDir===&amp;#039;asc&amp;#039; ? c : -c;&lt;br /&gt;
          });&lt;br /&gt;
&lt;br /&gt;
          tbody.textContent=&amp;#039;&amp;#039;;&lt;br /&gt;
          filtered.forEach(function(r){&lt;br /&gt;
            var tr=document.createElement(&amp;#039;tr&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
            var tdCat=document.createElement(&amp;#039;td&amp;#039;);&lt;br /&gt;
            tdCat.className=&amp;#039;mm-td-category&amp;#039;;&lt;br /&gt;
            tdCat.textContent=r.category;&lt;br /&gt;
            tr.appendChild(tdCat);&lt;br /&gt;
&lt;br /&gt;
            var tdName=document.createElement(&amp;#039;td&amp;#039;);&lt;br /&gt;
            tdName.className=&amp;#039;mm-td-name&amp;#039;;&lt;br /&gt;
            tdName.textContent=r.name;&lt;br /&gt;
            if(r.detail) tdName.title=r.detail;&lt;br /&gt;
            tr.appendChild(tdName);&lt;br /&gt;
&lt;br /&gt;
            var tdNpc=document.createElement(&amp;#039;td&amp;#039;);&lt;br /&gt;
            tdNpc.textContent=r.npc || &amp;#039;—&amp;#039;;&lt;br /&gt;
            tr.appendChild(tdNpc);&lt;br /&gt;
&lt;br /&gt;
            var tdPurpose=document.createElement(&amp;#039;td&amp;#039;);&lt;br /&gt;
            tdPurpose.textContent=r.purpose || &amp;#039;—&amp;#039;;&lt;br /&gt;
            if(r.detail) tdPurpose.title=r.detail;&lt;br /&gt;
            tr.appendChild(tdPurpose);&lt;br /&gt;
&lt;br /&gt;
            var tdLink=document.createElement(&amp;#039;td&amp;#039;);&lt;br /&gt;
            if(r.href){&lt;br /&gt;
              var a=document.createElement(&amp;#039;a&amp;#039;);&lt;br /&gt;
              a.href=r.href;&lt;br /&gt;
              a.target=&amp;#039;_blank&amp;#039;;&lt;br /&gt;
              a.rel=&amp;#039;noopener&amp;#039;;&lt;br /&gt;
              a.textContent=&amp;#039;Open&amp;#039;;&lt;br /&gt;
              tdLink.appendChild(a);&lt;br /&gt;
            }else{&lt;br /&gt;
              tdLink.textContent=&amp;#039;—&amp;#039;;&lt;br /&gt;
            }&lt;br /&gt;
            tr.appendChild(tdLink);&lt;br /&gt;
&lt;br /&gt;
            tbody.appendChild(tr);&lt;br /&gt;
          });&lt;br /&gt;
&lt;br /&gt;
          if(metaEl) metaEl.textContent=filtered.length+&amp;#039; / &amp;#039;+rows.length;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var categories={};&lt;br /&gt;
        rows.forEach(function(r){ categories[r.category]=true; });&lt;br /&gt;
        Object.keys(categories).sort().forEach(function(c){&lt;br /&gt;
          var opt=document.createElement(&amp;#039;option&amp;#039;);&lt;br /&gt;
          opt.value=c;&lt;br /&gt;
          opt.textContent=c;&lt;br /&gt;
          categoryEl.appendChild(opt);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        table.querySelectorAll(&amp;#039;th[data-key]&amp;#039;).forEach(function(th){&lt;br /&gt;
          th.tabIndex=0;&lt;br /&gt;
          th.addEventListener(&amp;#039;click&amp;#039;, function(){&lt;br /&gt;
            var k=th.getAttribute(&amp;#039;data-key&amp;#039;);&lt;br /&gt;
            if(!k) return;&lt;br /&gt;
            if(sortKey===k){ sortDir = sortDir===&amp;#039;asc&amp;#039; ? &amp;#039;desc&amp;#039; : &amp;#039;asc&amp;#039;; }&lt;br /&gt;
            else { sortKey=k; sortDir=&amp;#039;asc&amp;#039;; }&lt;br /&gt;
            apply();&lt;br /&gt;
          });&lt;br /&gt;
          th.addEventListener(&amp;#039;keydown&amp;#039;, function(e){&lt;br /&gt;
            if(e.key===&amp;#039;Enter&amp;#039; || e.key===&amp;#039; &amp;#039;){ e.preventDefault(); th.click(); }&lt;br /&gt;
          });&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        if(searchEl) searchEl.addEventListener(&amp;#039;input&amp;#039;, apply);&lt;br /&gt;
        if(categoryEl) categoryEl.addEventListener(&amp;#039;change&amp;#039;, apply);&lt;br /&gt;
        apply();&lt;br /&gt;
      }&lt;br /&gt;
      function refreshMapsIn(root){&lt;br /&gt;
        (root||document).querySelectorAll(&amp;#039;.mindmap&amp;#039;).forEach(function(m){&lt;br /&gt;
          if(m.__mmApi){&lt;br /&gt;
            if(m.offsetParent===null) return;&lt;br /&gt;
            m.__mmApi.layout();&lt;br /&gt;
            m.__mmApi.draw();&lt;br /&gt;
          }&lt;br /&gt;
        });&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      function initAll(){&lt;br /&gt;
        decorateLinkNodes(document);&lt;br /&gt;
        document.querySelectorAll(&amp;#039;.mindmap&amp;#039;).forEach(function(m){ initMap(m); });&lt;br /&gt;
        renderLocationsTables();&lt;br /&gt;
        initServicesTable();&lt;br /&gt;
        refreshMapsIn(document);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if(document.readyState===&amp;#039;loading&amp;#039;){&lt;br /&gt;
        document.addEventListener(&amp;#039;DOMContentLoaded&amp;#039;, initAll);&lt;br /&gt;
      }else{&lt;br /&gt;
        initAll();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      document.addEventListener(&amp;#039;click&amp;#039;,function(e){&lt;br /&gt;
        var tabBtn=e.target.closest(&amp;#039;.nav-tab&amp;#039;);&lt;br /&gt;
        if(!tabBtn) return;&lt;br /&gt;
        var tabId=tabBtn.getAttribute(&amp;#039;data-tab&amp;#039;);&lt;br /&gt;
        setTimeout(function(){&lt;br /&gt;
          if(tabId){&lt;br /&gt;
            var tab=document.getElementById(tabId);&lt;br /&gt;
            if(tab) refreshMapsIn(tab);&lt;br /&gt;
          }else{&lt;br /&gt;
            refreshMapsIn(document);&lt;br /&gt;
          }&lt;br /&gt;
        },250);&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      document.addEventListener(&amp;#039;click&amp;#039;,function(e){&lt;br /&gt;
        var nested=e.target.closest(&amp;#039;.nested-tab&amp;#039;);&lt;br /&gt;
        if(!nested) return;&lt;br /&gt;
        var id=nested.getAttribute(&amp;#039;data-tab&amp;#039;);&lt;br /&gt;
        setTimeout(function(){&lt;br /&gt;
          if(id){&lt;br /&gt;
            var el=document.getElementById(id);&lt;br /&gt;
            if(el) refreshMapsIn(el);&lt;br /&gt;
          }else{&lt;br /&gt;
            refreshMapsIn(document);&lt;br /&gt;
          }&lt;br /&gt;
        },250);&lt;br /&gt;
      });&lt;br /&gt;
      document.querySelectorAll(&amp;#039;.destaque-card&amp;#039;).forEach(function(card){&lt;br /&gt;
        var target=card.getAttribute(&amp;#039;data-node-target&amp;#039;); var tabBtn=card.getAttribute(&amp;#039;data-tab-button&amp;#039;); var tabId=card.getAttribute(&amp;#039;data-tab-trigger&amp;#039;);&lt;br /&gt;
        if(target){ card.addEventListener(&amp;#039;click&amp;#039;,function(){&lt;br /&gt;
          var btn=document.getElementById(tabBtn); if(btn)btn.click();&lt;br /&gt;
          setTimeout(function(){&lt;br /&gt;
            var targetEl=document.getElementById(target);&lt;br /&gt;
            if(!targetEl) return;&lt;br /&gt;
            var node=targetEl;&lt;br /&gt;
            if(targetEl.classList.contains(&amp;#039;mm-branch&amp;#039;)){&lt;br /&gt;
              node=targetEl.querySelector(&amp;#039;:scope &amp;gt; .mm-node&amp;#039;);&lt;br /&gt;
              if(node &amp;amp;&amp;amp; !targetEl.classList.contains(&amp;#039;open&amp;#039;)) targetEl.classList.add(&amp;#039;open&amp;#039;);&lt;br /&gt;
            }&lt;br /&gt;
            if(!node) return;&lt;br /&gt;
            var mindmap=node.closest(&amp;#039;.mindmap&amp;#039;);&lt;br /&gt;
            var api=mindmap &amp;amp;&amp;amp; mindmap.__mmApi ? mindmap.__mmApi : null;&lt;br /&gt;
            var branch=node.closest(&amp;#039;.mm-branch&amp;#039;);&lt;br /&gt;
            var isHead=branch &amp;amp;&amp;amp; branch.querySelector(&amp;#039;:scope &amp;gt; .mm-node&amp;#039;)===node;&lt;br /&gt;
            var inChildren=node.parentElement &amp;amp;&amp;amp; node.parentElement.classList &amp;amp;&amp;amp; node.parentElement.classList.contains(&amp;#039;mm-children&amp;#039;);&lt;br /&gt;
            if(branch &amp;amp;&amp;amp; (inChildren || isHead) &amp;amp;&amp;amp; !branch.classList.contains(&amp;#039;open&amp;#039;)) branch.classList.add(&amp;#039;open&amp;#039;);&lt;br /&gt;
            if(api){ api.layout(); api.draw(); api.showDetail(node); }&lt;br /&gt;
          },200);&lt;br /&gt;
        }); }&lt;br /&gt;
      });&lt;br /&gt;
    })();&lt;/div&gt;</summary>
		<author><name>Noorisei</name></author>
	</entry>
</feed>