diff --git a/static/js/map.js b/static/js/map.js
index fdedad74c5a6a521da85d10eb50c4f7e775a11dc..39d8f37e0cc4b5f7daaaee28b132c6ba579462e8 100644
--- a/static/js/map.js
+++ b/static/js/map.js
@@ -2,8 +2,7 @@
 var BusMap = {
     cookiePrefix: "BM_",
     zoomShowVehicles: 15,
-    vehicleZoom: 16,
-    stopZoom: 16,
+    stopZoom: 18,
     vehicleMaxAge: 10,
 };
 
@@ -12,12 +11,12 @@ var BusMap = {
     Updating the map (Vehicles, Routes) is also handled here.
 */
 BusMap.Map = function(opts) {
-    this.opts = opts;
     var stops = {};
     var routes = {};
     var modal_cache = {};
-    var that = this;
-    window._busmap = this;
+    var that = this.Map;
+    that.opts = opts;
+    window._busmap = this.Map;
 
     /* Constructor - create/initialize the map */
     function init() {
@@ -27,7 +26,8 @@ BusMap.Map = function(opts) {
             animate: false,
             reset: true,
         };
-        that.leaflet = L.map(that.opts.mapElement, mapOptions)
+        that.map = that.opts.mapElement;
+        that.leaflet = L.map(that.map, mapOptions)
             .fitBounds(that.opts.bounds)
             .setMaxBounds(that.opts.bounds, boundsOptions);
         if (that.opts.center) { that.leaflet.setView(that.opts.center); }
@@ -35,9 +35,7 @@ BusMap.Map = function(opts) {
         if (that.opts.zoom) that.leaflet.setZoom(that.opts.zoom);
 
         // Listen for hash change
-        window.onhashchange = function() {
-            setViewFromUrl();
-        };
+        $(window).on('hashchange', setViewFromUrl);
 
         // Go to view requested by URL hash (if set)
         var viewOk = setViewFromUrl();
@@ -49,9 +47,9 @@ BusMap.Map = function(opts) {
 
         // Store view parameters for recovery later.
         that.leaflet.on('moveend', lastViewStore);
-        that.leaflet.on('moveend', function() {
-            setUrlFromView();
-        });
+        if (that.opts.hashUpdate !== false) {
+            that.leaflet.on('moveend', setUrlFromView);
+        }
 
 
         // Show/hide markers based on zoom.
@@ -62,7 +60,7 @@ BusMap.Map = function(opts) {
         var tileOptions = {
         };
         if (that.opts.tileOptions) {
-            for (o in that.opts.tileOptions) {
+            for (var o in that.opts.tileOptions) {
                 tileOptions[o] = that.opts.tileOptions[o];
             }
         }
@@ -148,7 +146,7 @@ BusMap.Map = function(opts) {
                     that.stops[s].predictions = {};
                 }
                 for (var p in data.predictions) {
-                    pr = data.predictions[p];
+                    var pr = data.predictions[p];
                     if (that.stops && pr.stop_id in that.stops) {
                         // Store this prediction with the relevant stop
                         if (!(pr.route in that.stops[pr.stop_id].predictions)) {
@@ -218,11 +216,11 @@ BusMap.Map = function(opts) {
                 var predictions = [];
                 var now = new Date();
                 var offset_mins = now.getTimezoneOffset();
-                psorted = vehicles[v].predictions.sort(function(a,b){
+                var psorted = vehicles[v].predictions.sort(function(a,b){
                     return new Date(a.prediction).getTime() - new Date(b.prediction).getTime();
                 });
-                for (p in psorted) {
-                    pr = psorted[p];
+                for (var p in psorted) {
+                    var pr = psorted[p];
                     var p_line = "<strong>" + that.stops[pr.stop_id].title + "</strong>: ";
                     var pdate = new Date(pr.prediction);
                     var diff_sec = (pdate.getTime() - now.getTime()) / 1000;
@@ -250,7 +248,7 @@ BusMap.Map = function(opts) {
         }
         // Remove stale markes from the map
         for (v in that.vehicleMarkers) {
-            var min_updated = Date.now() - (that.vehicleMaxAge * 1000)
+            var min_updated = Date.now() - (BusMap.vehicleMaxAge * 1000)
             if (that.vehicleMarkers[v].bm_updated < min_updated) {
                 that.leaflet.removeLayer(that.vehicleMarkers[v]);
                 delete that.vehicleMarkers[v];
@@ -298,11 +296,11 @@ BusMap.Map = function(opts) {
                 that.stopMarkersClusterGroup.addLayer(that.stopMarkers[s]);
             }
             // Add predictions to the marker popup, if available
-            if (stops[s].predictions) {
+            if (stops[s].predictions && that.opts.predictions !== false) {
                 var predictions = [];
                 var now = new Date();
                 var offset_mins = now.getTimezoneOffset();
-                for (r in stops[s].predictions) {
+                for (var r in stops[s].predictions) {
                     if (!(r in that.routes)) {
                         console.log("Unknown route " + r + " for stop " + stops[s].title);
                     }
@@ -312,7 +310,7 @@ BusMap.Map = function(opts) {
                     psorted = stops[s].predictions[r].sort(function(a,b){
                         return new Date(a.prediction).getTime() - new Date(b.prediction).getTime();
                     });
-                    for (p in psorted) {
+                    for (var p in psorted) {
                         pr = psorted[p];
                         var pdate = new Date(pr.prediction);
                         var diff_sec = (pdate.getTime() - now.getTime()) / 1000;
@@ -359,7 +357,7 @@ BusMap.Map = function(opts) {
             var marker = that.stopMarkers[view.substring(1)];
             if (marker) {
                 var ll = marker.getLatLng();
-                that.leaflet.setView(ll, that.stopZoom);
+                that.leaflet.setView(ll, BusMap.stopZoom);
             } else console.log('Unknown stop in view string: ' + view.substring(1));
             if (view.charAt(0) == "S") {
                 marker.openPopup();
@@ -394,7 +392,11 @@ BusMap.Map = function(opts) {
             $(this).parent().remove();
             setUrlFromView();
         });
-        $("#map").after(modal);
+        $(that.map).click(function() {
+            modal.remove();
+            setUrlFromView;
+        });
+        $(that.opts.mapElement).after(modal);
         modal.show();
         var params = {
             agency: that.opts.agency,
@@ -419,11 +421,11 @@ BusMap.Map = function(opts) {
     function zoomShowHide() {
         var zoom = that.leaflet.getZoom();
         if (that.vehicleMarkersGroup) {
-            if (zoom >= that.zoomShowVehicles
+            if (zoom >= BusMap.zoomShowVehicles
               && !(that.leaflet.hasLayer(that.vehicleMarkersGroup))) {
                 that.leaflet.addLayer(that.vehicleMarkersGroup);
                 if (!that.opts.embed) $('#msg-zoomForVehicles').hide();
-            } else if (zoom < that.zoomShowVehicles
+            } else if (zoom < BusMap.zoomShowVehicles
               && that.leaflet.hasLayer(that.vehicleMarkersGroup)) {
                 that.leaflet.removeLayer(that.vehicleMarkersGroup);
                 if (!that.opts.embed) $('#msg-zoomForVehicles').show();
@@ -437,6 +439,9 @@ BusMap.Map = function(opts) {
             delete that['hashChangedProgrammatically'];
             return;
         }
+        if (that.opts.hashListen == false) {
+            return;
+        }
         var hash = window.location.hash.substring(1);
         if (hash == "") return;
         var parts = hash.split(";");
@@ -449,6 +454,9 @@ BusMap.Map = function(opts) {
     }
 
     function setUrlFromView() {
+        if (that.opts.hashUpdate == false) {
+            return;
+        }
         var view = getViewString();
         if ($(".modal").length > 0) {
             var modalName = $(".modal").attr('id').split('-')[1];
@@ -462,7 +470,7 @@ BusMap.Map = function(opts) {
             return;
         }
         that.hashChangedProgrammatically = true; // avoid infinite loop!
-        window.location.href.replace(window.location.hash, "#" + view);
+        window.location = window.location.href.replace(window.location.hash, "#" + view);
     }
 
     init();
diff --git a/static/js/predictions.js b/static/js/predictions.js
index 962ead9b9002975f4b64156fc71b6164295bac2d..8f85012fdbdbd7e74e815c6c669337bd22b4c1b0 100644
--- a/static/js/predictions.js
+++ b/static/js/predictions.js
@@ -1,8 +1,9 @@
 BusMap.Predictions = function(opts) {
-    this.opts = opts;
     var stops = {};
     var routes = {};
-    var that = this;
+    var that = this.Predictions;
+    that.opts = opts;
+    window._buspredictions = this.Predictions;
 
     /* Constructor - create/initialize the map */
     function init() {
@@ -19,7 +20,9 @@ BusMap.Predictions = function(opts) {
         }
 
         // Listen for hash change
-        window.onhashchange = setViewFromUrl;
+        if (that.opts.hashListen !== false) {
+            $(window).on('hashchange', setViewFromUrl);
+        }
 
         // Grab initial URL
         setViewFromUrl();
@@ -60,7 +63,7 @@ BusMap.Predictions = function(opts) {
                     that.stops[s].predictions = {};
                 }
                 for (var p in data.predictions) {
-                    pr = data.predictions[p];
+                    var pr = data.predictions[p];
                     if (that.stops && pr.stop_id in that.stops) {
                         // Store this prediction with the relevant stop
                         if (!(pr.route in that.stops[pr.stop_id].predictions)) {
@@ -95,7 +98,6 @@ BusMap.Predictions = function(opts) {
 
     function setViewFromUrl() {
         var hash = window.location.hash.substring(1);
-        console.log(hash);
         if (hash == "") {
             $(that.opts.predictionsElement).html("No stop selected");
         } else applyViewString(hash);
@@ -116,18 +118,18 @@ BusMap.Predictions = function(opts) {
             var predictions = [];
             var now = new Date();
             var offset_mins = now.getTimezoneOffset();
-            for (r in stops[s].predictions) {
+            for (var r in stops[s].predictions) {
                 if (!(r in that.routes)) {
                     console.log("Unknown route " + r + " for stop " + stops[s].title);
                 }
                 var p_line = "<strong>" + that.routes[r].title + "</strong>: ";
                 var times = [];
                 // Sort by estimated time to arrival
-                psorted = stops[s].predictions[r].sort(function(a,b){
+                var psorted = stops[s].predictions[r].sort(function(a,b){
                     return new Date(a.prediction).getTime() - new Date(b.prediction).getTime();
                 });
-                for (p in psorted) {
-                    pr = psorted[p];
+                for (var p in psorted) {
+                    var pr = psorted[p];
                     var pdate = new Date(pr.prediction);
                     var diff_sec = (pdate.getTime() - now.getTime()) / 1000;
                     var diff_min = Math.ceil(diff_sec / 60) + offset_mins;
@@ -144,9 +146,9 @@ BusMap.Predictions = function(opts) {
                 predictions.push("<span class='route'" + p_line + "</span>");
             }
             if (predictions.length == 0) {
-                predictions = ['<span class="none">No arrival predictions.</span>'];
+                var predictions = ['<span class="none">No arrival predictions.</span>'];
             }
-            text = '<section class="predictions stop-predictions">'
+            var text = '<section class="predictions stop-predictions">'
                   + predictions.sort().join("<br>") + '</section>';
             $(that.opts.predictionsElement).find('p').html(text);
         }