const membersApiUrl = '/horde-members/api/members'; const featuresApiUrl = '/horde-members/api/features'; const apiKey = 'eyJhbGciOiJIUzUxMiIsImlhdCI6MTU1Mzg5MjYwNSwiZXhwIjoxODY5MjUyNjA1fQ.ImNsaWVudCI.iX2EEEsKbMYpbeU-HB_FcqeepwZn8rkq8XdyfUmr6RJk2-64I744xLdKfrikxskF6_IlJbjBH3jNNcVfWyvswQ'; const discordClientId = '562565397451374593'; const cheeseHordeGuildId = '285154599038353408'; var discordUser = undefined; function store(feature) { var writer = new ol.format.GeoJSON({ dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' }); $.ajax({ url: featuresApiUrl, type: 'post', contentType: 'application/json', data: writer.writeFeature(feature), headers: {'X-Members-API-Key': apiKey} }); } function remove(id) { $.ajax({ url: `${membersApiUrl}/${id}`, type: 'delete', headers: {'X-Members-API-Key': apiKey} }); } function removePin() { if (!discordUser) return; var feature = source.forEachFeature((feature) => { if (feature.get('id') == discordUser.id) return feature; }); if (feature === undefined) return; if (confirm('Are you sure you want to remove yourself from the map?')) { source.removeFeature(feature); remove(discordUser.id); var button = editControl.element.firstElementChild; button.style.display = 'none'; var button = removeControl.element.firstElementChild; button.style.display = 'none'; } } function editNick(feature) { if (!discordUser) return; if (!feature) feature = source.forEachFeature((feature) => { if (feature.get('id') == discordUser.id) return feature; }); if (feature === undefined) return; var nick = prompt('Edit nickname:', feature.get('nick')); if (nick !== null && nick != feature.get('nick')) { feature.set('nick', nick); store(feature); } } document.addEventListener('keydown', (evt) => { if (evt.code == 'Delete') removePin(); }); function getColor(nick, alpha) { var r = 0; var g = 0; var b = 0; var p1 = nick.length * 351 / 1000 >> 0; var p2 = p1 + nick.length * 206 / 1000 >> 0; nick.slice(0, p1).split('').map((c) => { r += c.charCodeAt(0) }); nick.slice(p1, p2).split('').map((c) => { g += c.charCodeAt(0) }); nick.slice(p2).split('').map((c) => { b += c.charCodeAt(0) }); r %= 256; g %= 256; b %= 256; var k = (299 * r + 587 * g + 114 * b) / 1000; while (k > 123) { if (r > 0) r--; if (g > 0) g--; if (b > 0) b--; k = (299 * r + 587 * g + 114 * b) / 1000; } return `rgba(${r}, ${g}, ${b}, ${alpha})`; } var discord = new LoginWithDiscord({ clientID: discordClientId, scopes: [ Scope.Identify, Scope.Guilds ], cache: true }); discord.onlogin = async () => { var user = await discord.fetchUser(); var guilds = await discord.fetchGuilds(); var guild = guilds.find((guild) => guild.id == cheeseHordeGuildId); if (guild !== undefined) { discordUser = user; var button = discordControl.element.firstElementChild; button.innerHTML = `${discordUser.username}: Log out`; button.style.display = null; var feature = source.forEachFeature((feature) => { if (feature.get('id') == discordUser.id) return feature; }); var button = editControl.element.firstElementChild; button.style.display = feature === undefined ? 'none' : null; var button = removeControl.element.firstElementChild; button.style.display = feature === undefined ? 'none' : null; } } discord.onlogout = async () => { discordUser = undefined; var button = discordControl.element.firstElementChild; button.innerHTML = 'Log in with Discord'; button.style.display = null; var button = editControl.element.firstElementChild; button.style.display = 'none'; var button = removeControl.element.firstElementChild; button.style.display = 'none'; }; var DiscordControl = (function(Control) { function DiscordControl(opt_options) { var options = opt_options || {}; var button = document.createElement('button'); button.style.display = 'none'; var element = document.createElement('div'); element.className = 'discord-control ol-unselectable ol-control'; element.appendChild(button); Control.call(this, { element: element, target: options.target }); button.addEventListener('click', this.handleClick.bind(this), false); } if (Control) DiscordControl.__proto__ = Control; DiscordControl.prototype = Object.create(Control && Control.prototype); DiscordControl.prototype.constructor = DiscordControl; DiscordControl.prototype.handleClick = async function() { if (!discordUser) await discord.login(); else await discord.logout(); }; return DiscordControl; }(ol.control.Control)); var discordControl = new DiscordControl(); var EditControl = (function(Control) { function EditControl(opt_options) { var options = opt_options || {}; var button = document.createElement('button'); button.style.display = 'none'; var element = document.createElement('div'); element.className = 'edit-control ol-unselectable ol-control'; element.appendChild(button); Control.call(this, { element: element, target: options.target }); button.addEventListener('click', this.handleClick.bind(this), false); } if (Control) EditControl.__proto__ = Control; EditControl.prototype = Object.create(Control && Control.prototype); EditControl.prototype.constructor = EditControl; EditControl.prototype.handleClick = function() { editNick(); }; return EditControl; }(ol.control.Control)); var editControl = new EditControl(); var RemoveControl = (function(Control) { function RemoveControl(opt_options) { var options = opt_options || {}; var button = document.createElement('button'); button.style.display = 'none'; var element = document.createElement('div'); element.className = 'remove-control ol-unselectable ol-control'; element.appendChild(button); Control.call(this, { element: element, target: options.target }); button.addEventListener('click', this.handleClick.bind(this), false); } if (Control) RemoveControl.__proto__ = Control; RemoveControl.prototype = Object.create(Control && Control.prototype); RemoveControl.prototype.constructor = RemoveControl; RemoveControl.prototype.handleClick = function() { removePin(); }; return RemoveControl; }(ol.control.Control)); var removeControl = new RemoveControl(); var source = new ol.source.Vector({ url: featuresApiUrl, wrapX: false, format: new ol.format.GeoJSON({ dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' }) }); var raster = new ol.layer.Tile({ source: new ol.source.OSM() }); var vector = new ol.layer.Vector({ source: source, style: (feature) => new ol.style.Style({ image: new ol.style.Circle({ radius: 6, fill: new ol.style.Fill({ color: getColor(feature.get('nick'), 0.8) }) }), text: new ol.style.Text({ text: feature.get('nick'), font: '12pt sans-serif', textAlign: 'left', offsetY: -20, fill: new ol.style.Fill({ color: 'rgba(0, 0, 0, 0.8)' }), backgroundFill: new ol.style.Fill({ color: 'rgba(255, 255, 255, 0.8)' }), backgroundStroke: new ol.style.Stroke({ color: 'rgba(0, 0, 0, 0.8)', width: 1 }), padding: [2, 3, 1, 3] }) }) }); var map = new ol.Map({ target: 'map', layers: [raster, vector], view: new ol.View({ center: [0, 0], minZoom: 2, zoom: 2 }), controls: ol.control.defaults().extend([ discordControl, editControl, removeControl ]) }); map.on('dblclick', (evt) => { if (!discordUser) return; map.forEachFeatureAtPixel(evt.pixel, (feature) => { if (feature.get('id') == discordUser.id) { editNick(feature); return true; } }); }); var draw = new ol.interaction.Draw({ source: source, type: 'Point', condition: (evt) => { if (!discordUser) return false; var feature = source.forEachFeature((feature) => { if (feature.get('id') == discordUser.id) return feature; }); return feature === undefined; } }); draw.on('drawend', (evt) => { evt.feature.set('id', discordUser.id); evt.feature.set('nick', discordUser.username); store(evt.feature); var button = editControl.element.firstElementChild; button.style.display = null; var button = removeControl.element.firstElementChild; button.style.display = null; // hack draw.lastDragTime_ = undefined; }); map.addInteraction(draw); var snap = new ol.interaction.Snap({ source: source }); map.addInteraction(snap); var modify = new ol.interaction.Modify({ source: source, condition: (evt) => { if (!discordUser) return false; var feature = source.getClosestFeatureToCoordinate(evt.coordinate); if (!feature) return false; return feature.get('id') == discordUser.id; } }); modify.on('modifyend', (evt) => { var feature = evt.features.getArray().find((feature) => feature.getRevision() > 1); store(feature); }); map.addInteraction(modify);