diff --git a/src/img/gym_blue.png b/src/img/gym_blue.png
new file mode 100644
index 0000000..9bc80d1
Binary files /dev/null and b/src/img/gym_blue.png differ
diff --git a/src/img/gym_blue_battle.png b/src/img/gym_blue_battle.png
new file mode 100644
index 0000000..4fdcd09
Binary files /dev/null and b/src/img/gym_blue_battle.png differ
diff --git a/src/img/gym_neutral.png b/src/img/gym_neutral.png
new file mode 100644
index 0000000..c8859c9
Binary files /dev/null and b/src/img/gym_neutral.png differ
diff --git a/src/img/gym_neutral_battle.png b/src/img/gym_neutral_battle.png
new file mode 100644
index 0000000..90a75c1
Binary files /dev/null and b/src/img/gym_neutral_battle.png differ
diff --git a/src/img/gym_red.png b/src/img/gym_red.png
new file mode 100644
index 0000000..54ea906
Binary files /dev/null and b/src/img/gym_red.png differ
diff --git a/src/img/gym_red_battle.png b/src/img/gym_red_battle.png
new file mode 100644
index 0000000..1066eb2
Binary files /dev/null and b/src/img/gym_red_battle.png differ
diff --git a/src/img/gym_yellow.png b/src/img/gym_yellow.png
new file mode 100644
index 0000000..fad481a
Binary files /dev/null and b/src/img/gym_yellow.png differ
diff --git a/src/img/gym_yellow_battle.png b/src/img/gym_yellow_battle.png
new file mode 100644
index 0000000..45b1b8f
Binary files /dev/null and b/src/img/gym_yellow_battle.png differ
diff --git a/src/img/pokestop.png b/src/img/pokestop.png
new file mode 100644
index 0000000..722a89c
Binary files /dev/null and b/src/img/pokestop.png differ
diff --git a/src/img/pokestop_cooldown.png b/src/img/pokestop_cooldown.png
new file mode 100644
index 0000000..c955d72
Binary files /dev/null and b/src/img/pokestop_cooldown.png differ
diff --git a/src/img/pokestop_cooldown_lure.png b/src/img/pokestop_cooldown_lure.png
new file mode 100644
index 0000000..e341af5
Binary files /dev/null and b/src/img/pokestop_cooldown_lure.png differ
diff --git a/src/img/pokestop_lure.png b/src/img/pokestop_lure.png
new file mode 100644
index 0000000..99a0a30
Binary files /dev/null and b/src/img/pokestop_lure.png differ
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..866d213
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/script.js b/src/script.js
new file mode 100644
index 0000000..363f21d
--- /dev/null
+++ b/src/script.js
@@ -0,0 +1,138 @@
+var websocketAddress = ((window.location.protocol === "https:") ? "wss:" : "ws:") + "//" + window.location.host + "/websocket";
+
+if (typeof String.prototype.contains === 'undefined') {
+ String.prototype.contains = function(it) {
+ return this.indexOf(it) != -1;
+ };
+}
+
+var TeamColor = { Neutral: 0, Blue: 1, Red: 2, Yellow: 3 };
+var FortRenderingType = { Default: 0, InternalTest: 1 };
+var FortSponsor = { UnsetSponsor: 0, Mcdonalds: 1, PokemonStore: 2 };
+var FortType = { Gym: 0, Checkpoint: 1 };
+
+function initMap() {
+ var map = new google.maps.Map(document.getElementById('map'), {
+ settings: {
+ draggable: false
+ }
+ });
+
+ var playerMarker = new google.maps.Marker({
+ map: map
+ });
+
+ var fortPath = new google.maps.Polyline({
+ path: [],
+ geodesic: true,
+ strokeColor: '#FF0000',
+ strokeOpacity: 1.0,
+ strokeWeight: 2
+ });
+
+ var playerPos;
+ var fortPathTarget;
+ var fortMarkers;
+ var intervalId;
+
+ var handleMessage = function(e) {
+ var message = JSON.parse(e.data);
+ var type = message.$type;
+ if(type.contains('UpdatePositionEvent')) {
+ var pos = {lat: message.Latitude, lng: message.Longitude};
+ if(playerPos.lat == 0 && playerPos.lng == 0) {
+ map.setCenter(pos);
+ playerMarker.setPosition(pos);
+ }
+ playerPos = pos;
+ } else if(type.contains('PokeStopListEvent')) {
+ var now = new Date();
+ for(var i in message.Forts.$values) {
+ var fort = message.Forts.$values[i];
+
+ if(!(fort.Id in fortMarkers)) {
+ fortMarkers[fort.Id] = new google.maps.Marker({
+ map: map
+ });
+ }
+
+ fortMarkers[fort.Id].setPosition({lat: fort.Latitude, lng: fort.Longitude});
+ if(fort.Type == FortType.Gym) {
+ if(fort.OwnedByTeam == TeamColor.Neutral) {
+ fortMarkers[fort.Id].setIcon('img/' + (fort.IsInBattle ? 'gym_neutral_battle.png' : 'gym_neutral.png'));
+ } else if(fort.OwnedByTeam == TeamColor.Red) {
+ fortMarkers[fort.Id].setIcon('img/' + (fort.IsInBattle ? 'gym_red_battle.png' : 'gym_red.png'));
+ } else if(fort.OwnedByTeam == TeamColor.Blue) {
+ fortMarkers[fort.Id].setIcon('img/' + (fort.IsInBattle ? 'gym_blue_battle.png' : 'gym_blue.png'));
+ } else if(fort.OwnedByTeam == TeamColor.Yellow) {
+ fortMarkers[fort.Id].setIcon('img/' + (fort.IsInBattle ? 'gym_yellow_battle.png' : 'gym_yellow.png'));
+ } else {
+ console.warn('Unknown FortData.OwnedByTeam: ' + fort.OwnedByTeam);
+ }
+ } else if(fort.Type == FortType.Checkpoint) {
+ fortMarkers[fort.Id].lure = fort.LureInfo == null;
+ if(new Date(fort.CooldownCompleteTimestampMs) < now) {
+ fortMarkers[fort.Id].setIcon('img/' + (fortMarkers[fort.Id].lure ? 'pokestop.png' : 'pokestop_lure.png'));
+ } else {
+ fortMarkers[fort.Id].setIcon('img/' + (fortMarkers[fort.Id].lure ? 'pokestop_cooldown.png' : 'pokestop_cooldown_lure.png'));
+ }
+ } else {
+ console.warn('Unknown FortData.Type: ' + fort.Type);
+ }
+ }
+ } else if(type.contains("FortTargetEvent")) {
+ fortPath.setMap(map);
+ fortPathTarget = { lat: message.Latitude, lng: message.Longitude };
+ fortPath.setPath([
+ { lat: playerMarker.position.lat(), lng: playerMarker.position.lng() },
+ fortPathTarget
+ ]);
+ } else if(type.contains("FortUsedEvent")) {
+ fortPath.setMap(null);
+ fortPathTarget = null;
+
+ fortMarkers[message.Id].setIcon('img/' + (fortMarkers[message.Id].lure ? 'pokestop_cooldown.png' : 'pokestop_cooldown_lure.png'));
+ } /* else {
+ console.warn('Unknown message: ' + message['$type']);
+ console.warn(message);
+ } */
+ };
+
+ var connect = function() {
+ var ws = new WebSocket(websocketAddress);
+ ws.onopen = function() {
+ map.setCenter(null);
+ map.setZoom(17);
+ playerMarker.setPosition(null);
+
+ playerPos = { lat: 0, lng: 0 };
+ fortPathTarget = null;
+ fortMarkers = {};
+
+ intervalId = setInterval(function(){
+ var oldPos = playerMarker.position;
+ var newPos = {
+ lat: ((oldPos.lat() * 30) + playerPos.lat) / 31,
+ lng: ((oldPos.lng() * 30) + playerPos.lng) / 31
+ };
+ map.setCenter(newPos);
+ playerMarker.setPosition(newPos);
+ if(fortPathTarget != null) {
+ fortPath.setPath([
+ newPos,
+ fortPathTarget
+ ]);
+ }
+ }, 50);
+ };
+ ws.onclose = function() {
+ console.log("onclose");
+
+ clearInterval(intervalId);
+
+ setTimeout(connect, 500);
+ };
+ ws.onmessage = handleMessage;
+ };
+ connect();
+}
\ No newline at end of file
diff --git a/src/style.css b/src/style.css
new file mode 100644
index 0000000..3ab9c04
--- /dev/null
+++ b/src/style.css
@@ -0,0 +1,10 @@
+html, body, #map, #connecting {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+
+ margin: 0;
+ padding: 0;
+}
\ No newline at end of file