window.app = (function() { var that = {}, coords, foundLocation = false, map, marker, username = localStorage.username || 'Anonymous', color = randomColor(), userlist, users = [], zoomLevel = 23; window.onload = setup(); document.body && window.onload(); function createUser(data) { var user = { id: data.id, name: data.name, color: data.color, }; if (!user.color) { user.color = [255, 0, 0]; } users.push(user); if(coords) { update({ name: username, color: color, coords: coords }); } updateUserList(); return user; } function getUserById(id) { return users.filter(function(user) { return user.id == id; })[0]; } function initMap() { navigator.geolocation.watchPosition(updateLocation, locationError, { enableHighAccuracy: true }); } function joinRoom(room) { if(that.room) { post(['leave', that.room]); } users.forEach(function(user) { if (user.marker) { map.removeLayer(user.marker); } }); users = []; that.room = room; post(['join', that.room]); if (coords) { update({ name: username, coords: coords }); } else { initMap(); } updateUserList(); } function locationError(err) { console.log(err); } function post(data) { that.ws.send(JSON.stringify(data)); } function randomColor() { var colors = [ [128, 0, 0], [0, 128, 0], [0, 0, 128], [128, 128, 0], [0, 128, 128], [128, 0, 128], [128, 64, 0], [64, 128, 0], [0, 128, 64], [0, 64, 128], [64, 0, 128], [128, 0, 64] ]; return colors[Math.floor(Math.random() * 12)]; } function randomName() { var name = ''; while(name.length < 14) { name += String.fromCharCode('A'.charCodeAt(0) + Math.floor(Math.random() * 25)); } return name; } function removeUser(id) { var user = getUserById(id); if (user) { if (user.marker) { map.removeLayer(user.marker); } users = users.filter(function(user) { user.id != id; }); } } function setup() { var host = (document.location.protocol == 'http:' ? 'ws' : 'wss') + '://' + document.location.host; that.ws = new WebSocket(host + '/ws'); that.ws.onopen = function () { window.onhashchange = function() { var name = document.location.hash.slice(1); if (name) { document.title = '#' + name; joinRoom(name); } else { document.location.href = '#' + randomName(); } } window.onhashchange(); }; that.ws.onmessage = function (event) { var data = JSON.parse(event.data); if (data[0] == 'leave') { removeUser(data[1]); } else if (data[0] == 'update') { data[2].id = data[1]; updateUserLocation(data[2]); } }; that.ws.onclose = function (event) { setTimeout(function() { setup(); }, 1000); }; } function update(data) { post(['update', that.room, data]); } function updateLocation(loc) { coords = [loc.coords.latitude, loc.coords.longitude]; var options = { title: username, icon: L.icon({ iconUrl: MarkerIcon({ label: username[0], color: [255, 255, 255], background: color }) }) }; if (!foundLocation) { if (!map) { L.mapbox.accessToken = config.mapboxAccessToken; map = L.mapbox.map('map', 'mapbox.streets') .setView(coords, zoomLevel); } marker = L.marker(coords, options) .bindPopup(options.title) .addTo(map); map.setView(coords, zoomLevel); foundLocation = true; updateUserList(); } else { map.removeLayer(marker); marker = L.marker(coords, options) .bindPopup(options.title) .addTo(map); } update({ name: username, coords: coords }); } function updateUserLocation(data) { var user = getUserById(data.id); if (!user) { user = createUser(data); } var options = { title: user.name, icon: L.icon({ iconUrl: MarkerIcon({ label: user.name[0], background: user.color }) }) }; user.coords = data.coords; if (user.marker) { map.removeLayer(user.marker); } user.marker = L.marker(user.coords, options) .bindPopup(user.name) .addTo(map); } function updateUserList() { var list = document.createElement('div'); list.id = 'users'; [{ name: username, color: color }].concat(users).forEach(function(user) { var div = document.createElement('div'); div.className = 'person'; div.innerHTML = user.name; div.style.background = 'rgb(' + user.color.join(', ') + ')'; div.style.top = '8px' list.appendChild(div); }); if (userlist) { document.body.replaceChild(list, userlist); } else { document.body.appendChild(list); } userlist = list; } function MarkerIcon(options) { options = options || {}; options.background = options.background || [255, 0, 0]; options.color = options.color || [255, 255, 255]; options.label = options.label || 'A'; var size = 20; var canvas = document.createElement('canvas') canvas.setAttribute('width', size); canvas.setAttribute('height', size); var context = canvas.getContext('2d'); context.fillStyle = 'rgb(' + options.background.join(', ') + ')'; context.arc(size / 2, size / 2, size / 2 - 2, 0, 360); context.fill(); context.beginPath(); context.lineWidth = 2; context.strokeStyle = 'rgb(' + options.color.join(', ') + ')'; context.arc(size / 2, size / 2, size / 2 - 1, 0, 360); context.stroke(); context.font = '14px sans-serif'; context.textAlign = 'center'; context.fillStyle = 'rgb(' + options.color.join(', ') + ')'; context.fillText(options.label, size / 2, size * 0.75); return canvas.toDataURL(); } return that; })();