{"version":3,"file":"bundle.js","sources":["api.js","LayerManager.js","WebSocketChannel.js","map/SimpleCRS.js","map/CoordinatesDisplay.js","components/WorldStats.js","map/WorldInfoDisplay.js","components/SearchInput.js","components/LayerSelector.js","config.js","map/TopRightControl.js","map/overlays/PlayerOverlay.js","util/debounce.js","map/overlays/AbstractIconOverlay.js","map/overlays/PoiOverlay.js","map/overlays/ShopOverlay.js","map/overlays/LabelOverlay.js","map/overlays/AbstractGeoJsonOverlay.js","map/overlays/TrainlineOverlay.js","map/overlays/TravelnetOverlay.js","map/overlays/BonesOverlay.js","map/overlays/LcdOverlay.js","map/overlays/DigitermOverlay.js","map/overlays/LuacontrollerOverlay.js","map/overlays/TechnicAnchorOverlay.js","map/overlays/TechnicQuarryOverlay.js","map/overlays/TechnicSwitchOverlay.js","map/overlays/ProtectorOverlay.js","map/overlays/XPProtectorOverlay.js","map/overlays/PrivProtectorOverlay.js","map/overlays/MissionOverlay.js","map/overlays/MinecartOverlay.js","map/overlays/ATMOverlay.js","map/overlays/LocatorOverlay.js","map/overlays/BorderOverlay.js","map/overlays/TrainOverlay.js","map/overlays/TrainsignalOverlay.js","map/Overlaysetup.js","map/CustomOverlay.js","map/RealtimeTileLayer.js","map/MapFactory.js","components/Map.js","components/SearchResult.js","components/Search.js","routes.js","compat.js","main.js"],"sourcesContent":["\nexport function getMapObjects(query){\n return m.request({\n method: \"POST\",\n url: \"api/mapobjects/\",\n data: query\n });\n}\n\nexport function getConfig(){\n return m.request(\"api/config\");\n}\n\nexport function getStats(){\n\treturn m.request(\"api/stats\");\n}\n","\nclass LayerManager {\n\n setup(layers){\n this.layers = layers;\n this.currentLayer = this.layers[0];\n }\n\n setLayerId(layerId){\n var self = this;\n this.layers.forEach(function(layer){\n if (layer.id == layerId){\n self.currentLayer = layer;\n return;\n }\n });\n\n if (layerId != this.currentLayer.id){\n // layer not found\n this.currentLayer = this.layers[0];\n }\n }\n\n getLayerByY(y){\n return this.layers.find(layer => (y >= (layer.from*16) && y <= (layer.to*16)));\n }\n\n getCurrentLayer(){\n return this.currentLayer;\n }\n}\n\nexport default new LayerManager();\n","\nimport { getStats } from './api.js';\n\nclass WebSocketChannel {\n constructor(){\n this.wsUrl = window.location.protocol.replace(\"http\", \"ws\") +\n \"//\" + window.location.host +\n window.location.pathname.substring(0, window.location.pathname.lastIndexOf(\"/\")) +\n \"/api/ws\";\n\n this.listenerMap = {/* type -> [listeners] */};\n }\n\n addListener(type, listener){\n var list = this.listenerMap[type];\n if (!list){\n list = [];\n this.listenerMap[type] = list;\n }\n\n list.push(listener);\n }\n\n removeListener(type, listener){\n var list = this.listenerMap[type];\n if (!list){\n return;\n }\n\n this.listenerMap[type] = list.filter(l => l != listener);\n }\n\n connect(){\n var ws = new WebSocket(this.wsUrl);\n var self = this;\n\n ws.onmessage = function(e){\n var event = JSON.parse(e.data);\n //rendered-tile, mapobject-created, mapobjects-cleared\n\n var listeners = self.listenerMap[event.type];\n if (listeners){\n listeners.forEach(function(listener){\n listener(event.data);\n });\n }\n };\n\n\t\tfunction fallbackPolling(){\n\t\t\tgetStats()\n\t\t\t.then(function(stats){\n\t\t\t\tif (!stats){\n\t\t\t\t\t// no stats (yet)\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tvar listeners = self.listenerMap[\"minetest-info\"];\n\t\t\t\tif (listeners){\n\t\t\t\t\tlisteners.forEach(function(listener){\n\t\t\t\t\t\tlistener(stats);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n ws.onerror = function(){\n //fallback to polling stats\n setInterval(fallbackPolling, 2000);\n };\n }\n}\n\nexport default new WebSocketChannel();\n","\nexport default L.Util.extend({}, L.CRS.Simple, {\n scale: function (zoom) {\n return Math.pow(2, zoom-9);\n }\n});\n","\nexport default L.Control.extend({\n onAdd: function(map) {\n var div = L.DomUtil.create('div', 'leaflet-bar leaflet-custom-display');\n\n var hoverCoord, clickCoord;\n\n function updateHover(ev){\n hoverCoord = ev.latlng;\n update();\n }\n\n function updateClick(ev){\n clickCoord = ev.latlng;\n update();\n }\n\n function update(){\n var html = \"\";\n if (hoverCoord)\n html = html + \"X=\" + parseInt(hoverCoord.lng) + \" Z=\" + parseInt(hoverCoord.lat);\n\n if (clickCoord)\n html = html + \" (marked: X=\" + parseInt(clickCoord.lng) + \" Z=\" + parseInt(clickCoord.lat) + \")\";\n\n div.innerHTML = html;\n }\n\n map.on('mousemove', updateHover);\n map.on('click', updateClick);\n map.on('touch', updateClick);\n\n return div;\n }\n});\n","\nexport default function(info){\n\n var timeIcon = m(\"span\", { class: \"fa fa-sun\", style: \"color: orange;\" });\n\n if (info.time < 5500 || info.time > 19000) //0 - 24'000\n timeIcon = m(\"span\", { class: \"fa fa-moon\", style: \"color: blue;\" });\n\n function getHour(){\n return Math.floor(info.time/1000);\n }\n\n function getMinute(){\n var min = Math.floor((info.time % 1000) / 1000 * 60);\n return min >= 10 ? min : \"0\" + min;\n }\n\n function getLag(){\n var color = \"green\";\n if (info.max_lag > 0.8)\n color = \"orange\";\n else if (info.max_lag > 1.2)\n color = \"red\";\n\n return [\n m(\"span\", { class: \"fa fa-wifi\", style: \"color: \" + color }),\n parseInt(info.max_lag*1000),\n \" ms\"\n ];\n }\n\n function getPlayers(){\n return [\n m(\"span\", { class: \"fa fa-users\" }),\n info.players ? info.players.length : \"0\"\n ];\n }\n\n return m(\"div\", [\n getPlayers(),\n \" \",\n getLag(),\n \" \",\n m(\"span\", { class: \"fa fa-clock\" }),\n timeIcon,\n getHour(), \":\", getMinute()\n ]);\n\n}\n","/* exported WorldInfoDisplay */\nimport WorldStats from '../components/WorldStats.js';\n\n// coord display\nexport default L.Control.extend({\n initialize: function(wsChannel, opts) {\n L.Control.prototype.initialize.call(this, opts);\n this.wsChannel = wsChannel;\n },\n\n onAdd: function() {\n var div = L.DomUtil.create('div', 'leaflet-bar leaflet-custom-display');\n\n this.wsChannel.addListener(\"minetest-info\", function(info){\n m.render(div, WorldStats(info));\n });\n\n return div;\n }\n});\n","\nconst state = {\n query: \"\"\n};\n\nfunction doSearch(){\n m.route.set(`/search/${state.query}`);\n}\n\nexport default {\n view: function(){\n\n function handleInput(e){\n state.query = e.target.value;\n }\n\n function handleKeyDown(e){\n if (e.keyCode == 13){\n doSearch();\n }\n }\n\n return m(\"div\", { class: \"input-group mb-3\" }, [\n m(\"input[type=text]\", {\n placeholder: \"Search\",\n class: \"form-control\",\n oninput: handleInput,\n onkeydown: handleKeyDown,\n value: state.query\n }),\n m(\"div\", { class: \"input-group-append\", onclick: doSearch }, [\n m(\"span\", { class: \"input-group-text\" }, [\n m(\"i\", { class: \"fa fa-search\"})\n ])\n ])\n ]);\n }\n};\n","import LayerManager from '../LayerManager.js';\n\nfunction onchange(e){\n const params = m.route.param();\n params.layerId = e.target.value;\n\n m.route.set(\"/map/:layerId/:zoom/:lon/:lat\", params);\n}\n\n\nexport default {\n view: function(){\n\n const layers = LayerManager.layers.map(layer => m(\n \"option\",\n { value: layer.id, selected: layer.id == LayerManager.getCurrentLayer().id },\n layer.name\n ));\n\n return m(\"select\", { class: \"form-control\", onchange: onchange },layers);\n }\n};\n","\nvar config;\n\nexport default {\n get(){\n return config;\n },\n\n set(cfg){\n config = cfg;\n }\n};\n","import SearchInput from '../components/SearchInput.js';\nimport LayerSelector from '../components/LayerSelector.js';\nimport config from '../config.js';\n\nconst Component = {\n view: function(){\n const cfg = config.get();\n\n return m(\"div\", [\n cfg.enablesearch ? m(SearchInput) : null,\n m(LayerSelector)\n ]);\n }\n};\n\n\nexport default L.Control.extend({\n initialize: function(wsChannel, opts) {\n L.Control.prototype.initialize.call(this, opts);\n },\n\n onAdd: function() {\n var div = L.DomUtil.create('div');\n m.mount(div, Component);\n return div;\n }\n});\n","import wsChannel from '../../WebSocketChannel.js';\nimport layerMgr from '../../LayerManager.js';\n\nlet players = [];\n\n//update players all the time\nwsChannel.addListener(\"minetest-info\", function(info){\n players = info.players || [];\n});\n\nvar PlayerIcon = L.icon({\n iconUrl: 'pics/sam.png',\n\n iconSize: [16, 32],\n iconAnchor: [8, 16],\n popupAnchor: [0, -16]\n});\n\nexport default L.LayerGroup.extend({\n initialize: function() {\n L.LayerGroup.prototype.initialize.call(this);\n\n this.currentObjects = {}; // name => marker\n\n this.reDraw = this.reDraw.bind(this);\n this.onMinetestUpdate = this.onMinetestUpdate.bind(this);\n },\n\n createPopup: function(player){\n var html = \"\" + player.name + \"\";\n html += \"
\" + lcd.attributes.text + \"\";\n }\n});\n","import AbstractIconOverlay from './AbstractIconOverlay.js';\n\nvar DigitermIcon = L.icon({\n iconUrl: 'pics/digiterms_beige_front.png',\n\n iconSize: [16, 16],\n iconAnchor: [8, 8],\n popupAnchor: [0, -16]\n});\n\nexport default AbstractIconOverlay.extend({\n initialize: function() {\n AbstractIconOverlay.prototype.initialize.call(this, \"digiterm\", DigitermIcon);\n },\n\n createPopup: function(lcd){\n return \"
\" + lcd.attributes.display_text + \"\";\n }\n});\n","import AbstractIconOverlay from './AbstractIconOverlay.js';\n\nvar LuacontrollerIcon = L.icon({\n iconUrl: 'pics/jeija_luacontroller_top.png',\n\n iconSize: [16, 16],\n iconAnchor: [8, 8],\n popupAnchor: [0, -16]\n});\n\nvar LuacontrollerBurntIcon = L.icon({\n iconUrl: 'pics/jeija_luacontroller_burnt_top.png',\n\n iconSize: [16, 16],\n iconAnchor: [8, 8],\n popupAnchor: [0, -16]\n});\n\nexport default AbstractIconOverlay.extend({\n initialize: function() {\n AbstractIconOverlay.prototype.initialize.call(this, \"luacontroller\");\n },\n\n getIcon: function(ctrl){\n if (ctrl.attributes.burnt)\n return LuacontrollerBurntIcon;\n else\n return LuacontrollerIcon;\n },\n\n createPopup: function(ctrl){\n return \"LuaController\" + (ctrl.attributes.burnt ? \"(Burnt)\" : \"\");\n }\n});\n","import AbstractIconOverlay from './AbstractIconOverlay.js';\n\nvar TechnicAnchorIcon = L.icon({\n iconUrl: 'pics/technic_admin_anchor.png',\n\n iconSize: [32, 32],\n iconAnchor: [16, 16],\n popupAnchor: [0, -32]\n});\n\nexport default AbstractIconOverlay.extend({\n initialize: function() {\n AbstractIconOverlay.prototype.initialize.call(this, \"technicanchor\", TechnicAnchorIcon);\n },\n\n createPopup: function(lcd){\n return \"
Owner: \" + lcd.attributes.owner + \"
\" +\n \"Radius: \" + lcd.attributes.radius + \"
\" +\n \"Locked: \" + lcd.attributes.locked + \"
\" +\n \"Enabled: \" + lcd.attributes.enabled + \"
\";\n }\n});\n","import AbstractIconOverlay from './AbstractIconOverlay.js';\n\nvar TechnicQuarryIcon = L.icon({\n iconUrl: 'pics/default_tool_mesepick.png',\n\n iconSize: [16, 16],\n iconAnchor: [8, 8],\n popupAnchor: [0, -16]\n});\n\nexport default AbstractIconOverlay.extend({\n initialize: function() {\n AbstractIconOverlay.prototype.initialize.call(this, \"technicquarry\", TechnicQuarryIcon);\n },\n\n createPopup: function(quarry){\n return \"Owner: \" + quarry.attributes.owner + \"
\" +\n \"Dug: \" + quarry.attributes.dug + \"
\" +\n \"Enabled: \" + quarry.attributes.enabled + \"
\";\n }\n});\n","import AbstractIconOverlay from './AbstractIconOverlay.js';\n\nvar TechnicSwitchIcon = L.icon({\n iconUrl: 'pics/technic_water_mill_top_active.png',\n\n iconSize: [16, 16],\n iconAnchor: [8, 8],\n popupAnchor: [0, -16]\n});\n\nexport default AbstractIconOverlay.extend({\n initialize: function() {\n AbstractIconOverlay.prototype.initialize.call(this, \"technicswitch\", TechnicSwitchIcon);\n },\n\n createPopup: function(sw){\n return \"Active: \" + sw.attributes.active + \"
\" +\n \"Channel: \" + sw.attributes.channel + \"
\" +\n \"Supply: \" + sw.attributes.supply + \"
\" +\n \"Demand: \" + sw.attributes.demand + \"
\";\n }\n});\n","import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';\n\nexport default AbstractGeoJsonOverlay.extend({\n initialize: function() {\n AbstractGeoJsonOverlay.prototype.initialize.call(this, \"protector\");\n },\n\n getMaxDisplayedZoom: function(){\n return 11;\n },\n\n createFeature: function(protector){\n return {\n \"type\":\"Feature\",\n \"geometry\": {\n \"type\":\"Polygon\",\n \"coordinates\":[[\n [protector.x-5,protector.z-5],\n [protector.x-5,protector.z+6],\n [protector.x+6,protector.z+6],\n [protector.x+6,protector.z-5],\n [protector.x-5,protector.z-5]\n ]]\n },\n \"properties\":{\n \"name\": protector.attributes.owner,\n \"popupContent\": protector.attributes.owner\n }\n };\n }\n});\n","import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';\n\nexport default AbstractGeoJsonOverlay.extend({\n initialize: function() {\n AbstractGeoJsonOverlay.prototype.initialize.call(this, \"xpprotector\");\n },\n\n createFeature: function(protector){\n return {\n \"type\":\"Feature\",\n \"geometry\": {\n \"type\":\"Polygon\",\n \"coordinates\":[[\n [protector.x-5,protector.z-5],\n [protector.x-5,protector.z+6],\n [protector.x+6,protector.z+6],\n [protector.x+6,protector.z-5],\n [protector.x-5,protector.z-5]\n ]]\n },\n \"properties\":{\n \"name\": protector.attributes.xpthreshold,\n \"popupContent\": protector.attributes.xpthreshold\n }\n };\n }\n});\n","import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';\n\nexport default AbstractGeoJsonOverlay.extend({\n initialize: function() {\n AbstractGeoJsonOverlay.prototype.initialize.call(this, \"privprotector\");\n },\n\n createFeature: function(protector){\n return {\n \"type\":\"Feature\",\n \"geometry\": {\n \"type\":\"Polygon\",\n \"coordinates\":[[\n [protector.x-5,protector.z-5],\n [protector.x-5,protector.z+6],\n [protector.x+6,protector.z+6],\n [protector.x+6,protector.z-5],\n [protector.x-5,protector.z-5]\n ]]\n },\n \"properties\":{\n \"name\": protector.attributes.priv,\n \"popupContent\": protector.attributes.priv\n }\n };\n }\n});\n","import AbstractIconOverlay from './AbstractIconOverlay.js';\n\nvar MissionIcon = L.icon({\n iconUrl: 'pics/mission_32px.png',\n\n iconSize: [32, 32],\n iconAnchor: [16, 16],\n popupAnchor: [0, -32]\n});\n\nexport default AbstractIconOverlay.extend({\n initialize: function() {\n AbstractIconOverlay.prototype.initialize.call(this, \"mission\", MissionIcon);\n },\n\n createPopup: function(mission){\n return \"Owner: \" + mission.attributes.owner + \"
\" +\n \"Name: \" + mission.attributes.name + \"
\" +\n \"Successcount: \" + mission.attributes.successcount + \"
\" +\n \"Failcount: \" + mission.attributes.failcount + \"
\";\n }\n});\n","import wsChannel from '../../WebSocketChannel.js';\nimport layerMgr from '../../LayerManager.js';\n\nlet minecarts = [];\n\n//update minecarts all the time\nwsChannel.addListener(\"minetest-info\", function(info){\n minecarts = info.minecarts || [];\n});\n\n\nexport default L.LayerGroup.extend({\n initialize: function() {\n L.LayerGroup.prototype.initialize.call(this);\n\n this.currentObjects = {}; // name => marker\n\n this.reDraw = this.reDraw.bind(this);\n this.onMinetestUpdate = this.onMinetestUpdate.bind(this);\n },\n\n createMarker: function(cart){\n\n var Icon = L.icon({\n iconUrl: \"pics/minecart_logo.png\",\n\n iconSize: [32, 32],\n iconAnchor: [16, 16],\n popupAnchor: [0, -32]\n });\n\n var marker = L.marker([cart.pos.z, cart.pos.x], {icon: Icon});\n var html = \"Minecart