Use our own hash implementation
authorJohn Firebaugh <john.firebaugh@gmail.com>
Mon, 11 Nov 2013 18:27:37 +0000 (10:27 -0800)
committerJohn Firebaugh <john.firebaugh@gmail.com>
Mon, 11 Nov 2013 22:42:59 +0000 (14:42 -0800)
We need more flexibility than L.Hash provides.

Vendorfile
app/assets/javascripts/application.js
app/assets/javascripts/index.js
app/assets/javascripts/leaflet.layers.js
app/assets/javascripts/leaflet.map.js.erb
app/assets/javascripts/osm.js.erb
app/assets/javascripts/router.js
vendor/assets/leaflet/leaflet.hash.js [deleted file]

index c1242beb7fd92e0ccd9eb9d6aca11d26795f6acd..ec3ea6a14195c671ce06ace0fc9de85e964d935d 100644 (file)
@@ -32,10 +32,6 @@ folder 'vendor/assets' do
     from 'git://github.com/jfirebaugh/leaflet-osm.git' do
       file 'leaflet.osm.js', 'leaflet-osm.js'
     end
-
-    from 'git://github.com/mlevans/leaflet-hash.git' do
-      file 'leaflet.hash.js', 'leaflet-hash.js'
-    end
   end
 
   folder 'ohauth' do
index f66f231efc3a7c008322a7d8a5045a0ab69c5afd..2c186c909226a89788088dce891be0312bf1e38b 100644 (file)
@@ -9,7 +9,6 @@
 //= require osm
 //= require leaflet
 //= require leaflet.osm
-//= require leaflet.hash
 //= require leaflet.map
 //= require leaflet.zoom
 //= require leaflet.locationfilter
index 6c1c1610f373461a37fe7ddacabf13d40ef0a206..2d930024721932b58c6cbd49c36c27785fddc4c7 100644 (file)
@@ -23,21 +23,7 @@ $(document).ready(function () {
 
   map.attributionControl.setPrefix('');
 
-  map.hash = L.hash(map);
-
-  $(window).on('popstate', function(e) {
-    // popstate is triggered when the hash changes as well as on actual navigation
-    // events. We want to update the hash on the latter and not the former.
-    if (e.originalEvent.state) {
-      map.hash.update();
-    }
-  });
-
-  map.updateLayers(params);
-
-  $(window).on("hashchange", function () {
-    map.updateLayers(OSM.mapParams());
-  });
+  map.updateLayers(params.layers);
 
   map.on("baselayerchange", function (e) {
     if (map.getZoom() > e.layer.options.maxZoom) {
@@ -110,9 +96,6 @@ $(document).ready(function () {
     var expiry = new Date();
     expiry.setYear(expiry.getFullYear() + 10);
     $.cookie("_osm_location", cookieContent(map), { expires: expiry });
-
-    // Trigger hash update on layer changes.
-    map.hash.onMapMove();
   });
 
   if (OSM.PIWIK) {
@@ -225,7 +208,7 @@ $(document).ready(function () {
   var history = OSM.History(map),
     note = OSM.Note(map);
 
-  OSM.route = OSM.Router({
+  OSM.route = OSM.Router(map, {
     "/":                           OSM.Index(map),
     "/search":                     OSM.Search(map),
     "/export":                     OSM.Export(map),
index fd493ee32c212d882b38bd6948ef07861c26cd55..2bb4c5c1b44f008cba6f3d115c6c20c8e652af35 100644 (file)
@@ -140,6 +140,7 @@ L.OSM.layers = function(options) {
           } else {
             map.removeLayer(layer);
           }
+          map.fire('overlaylayerchange', {layer: layer});
         });
 
         map.on('layeradd layerremove', function() {
index 8a0d537b5125d644160ea1b2bc32e84dbb30cfc8..63fab53064d05261270bf243374cd41244ad7b6f 100644 (file)
@@ -56,8 +56,8 @@ L.OSM.Map = L.Map.extend({
     this.dataLayer.options.code = 'D';
   },
 
-  updateLayers: function(params) {
-    var layerParam = params.layers || "M";
+  updateLayers: function(layerParam) {
+    layerParam = layerParam || "M";
     var layersAdded = "";
 
     for (var i = this.baseLayers.length - 1; i >= 0; i--) {
@@ -243,9 +243,6 @@ L.extend(L.Icon.Default.prototype, {
   }
 });
 
-L.Hash.prototype.parseHash = OSM.parseHash;
-L.Hash.prototype.formatHash = OSM.formatHash;
-
 function getUserIcon(url) {
   return L.icon({
     iconUrl: url || <%= asset_path('marker-red.png').to_json %>,
index ad6d5cb9cd0785fe3b9b41c892ee93b8d0b0798e..cb37373518341fb44807973da75bf0732dc53fff 100644 (file)
@@ -101,15 +101,41 @@ OSM = {
   },
 
   parseHash: function(hash) {
-    if (hash.indexOf('#') === 0) {
-      hash = hash.substr(1);
+    var i = hash.indexOf('#');
+    if (i < 0) {
+      return false;
     }
+
+    hash = hash.substr(i + 1);
+
     if (hash === '') {
       return false;
     }
+
     hash = querystring.parse(hash);
-    var args = L.Hash.parseHash(hash.map || '') || {};
-    if (hash.layers) args.layers = hash.layers;
+
+    var args = hash.map.split("/");
+    if (args.length !== 3) {
+      return false;
+    }
+
+    var zoom = parseInt(args[0], 10),
+      lat = parseFloat(args[1]),
+      lon = parseFloat(args[2]);
+
+    if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
+      return false;
+    }
+
+    args = {
+      center: new L.LatLng(lat, lon),
+      zoom: zoom
+    };
+
+    if (hash.layers) {
+      args.layers = hash.layers;
+    }
+
     return args;
   },
 
index 7a61af106df9696b36ebc4bb68a8cda13be12da7..60630898297a67b31222ba43b04665eb11e56f72 100644 (file)
@@ -1,4 +1,4 @@
-OSM.Router = function(rts) {
+OSM.Router = function(map, rts) {
   var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
   var optionalParam = /\((.*?)\)/g;
   var namedParam    = /(\(\?)?:\w+/g;
@@ -45,14 +45,39 @@ OSM.Router = function(rts) {
   };
 
   var currentPath = window.location.pathname + window.location.search,
-    currentRoute = routes.recognize(currentPath);
+    currentRoute = routes.recognize(currentPath),
+    currentHash = location.hash || OSM.formatHash(map);
 
   currentRoute.run('load', currentPath);
 
+  var stateChange;
+
+  map.on('moveend baselayerchange overlaylayerchange', function() {
+    var hash = OSM.formatHash(map);
+    if (hash === currentHash) return;
+    currentHash = hash;
+    stateChange(OSM.parseHash(hash), hash);
+  });
+
+  $(window).on('hashchange', function() {
+    var hash = location.hash;
+    if (hash === currentHash) return;
+    currentHash = hash;
+    var state = OSM.parseHash(hash);
+    if (!state) return;
+    map.setView(state.center, state.zoom);
+    map.updateLayers(state.layers);
+    stateChange(state, hash);
+  });
+
   if (window.history && window.history.pushState) {
+    stateChange = function(state, hash) {
+      window.history.replaceState(state, document.title, hash);
+    };
+
     // Set a non-null initial state, so that the e.originalEvent.state
     // check below works correctly when going back to the initial page.
-    window.history.replaceState({}, document.title, window.location);
+    stateChange(OSM.parseHash(currentHash), currentPath + currentHash);
 
     $(window).on('popstate', function(e) {
       if (!e.originalEvent.state) return; // Is it a real popstate event or just a hash change?
@@ -62,13 +87,16 @@ OSM.Router = function(rts) {
       currentPath = path;
       currentRoute = routes.recognize(currentPath);
       currentRoute.run('popstate', currentPath);
+      var state = e.originalEvent.state;
+      map.setView(state.center, state.zoom);
+      map.updateLayers(state.layers);
     });
 
     return function (url) {
       var path = url.replace(/#.*/, ''),
         route = routes.recognize(path);
       if (!route) return false;
-      window.history.pushState({}, document.title, url);
+      window.history.pushState(OSM.parseHash(url) || {}, document.title, url);
       currentRoute.run('unload');
       currentPath = path;
       currentRoute = route;
@@ -76,6 +104,10 @@ OSM.Router = function(rts) {
       return true;
     }
   } else {
+    stateChange = function(state, hash) {
+      window.location.replace(hash);
+    };
+
     return function (url) {
       window.location.assign(url);
     }
diff --git a/vendor/assets/leaflet/leaflet.hash.js b/vendor/assets/leaflet/leaflet.hash.js
deleted file mode 100644 (file)
index 26bb8ab..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-(function(window) {
-       var HAS_HASHCHANGE = (function() {
-               var doc_mode = window.documentMode;
-               return ('onhashchange' in window) &&
-                       (doc_mode === undefined || doc_mode > 7);
-       })();
-
-       L.Hash = function(map) {
-               this.onHashChange = L.Util.bind(this.onHashChange, this);
-
-               if (map) {
-                       this.init(map);
-               }
-       };
-
-       L.Hash.parseHash = function(hash) {
-               if(hash.indexOf('#') === 0) {
-                       hash = hash.substr(1);
-               }
-               var args = hash.split("/");
-               if (args.length == 3) {
-                       var zoom = parseInt(args[0], 10),
-                       lat = parseFloat(args[1]),
-                       lon = parseFloat(args[2]);
-                       if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
-                               return false;
-                       } else {
-                               return {
-                                       center: new L.LatLng(lat, lon),
-                                       zoom: zoom
-                               };
-                       }
-               } else {
-                       return false;
-               }
-       };
-
-       L.Hash.formatHash = function(map) {
-               var center = map.getCenter(),
-                   zoom = map.getZoom(),
-                   precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
-
-               return "#" + [zoom,
-                       center.lat.toFixed(precision),
-                       center.lng.toFixed(precision)
-               ].join("/");
-       },
-
-       L.Hash.prototype = {
-               map: null,
-               lastHash: null,
-
-               parseHash: L.Hash.parseHash,
-               formatHash: L.Hash.formatHash,
-
-               init: function(map) {
-                       this.map = map;
-
-                       // reset the hash
-                       this.lastHash = null;
-                       this.onHashChange();
-
-                       if (!this.isListening) {
-                               this.startListening();
-                       }
-               },
-
-               remove: function() {
-                       if (this.changeTimeout) {
-                               clearTimeout(this.changeTimeout);
-                       }
-
-                       if (this.isListening) {
-                               this.stopListening();
-                       }
-
-                       this.map = null;
-               },
-
-               onMapMove: function() {
-                       // bail if we're moving the map (updating from a hash),
-                       // or if the map is not yet loaded
-
-                       if (this.movingMap || !this.map._loaded) {
-                               return false;
-                       }
-
-                       var hash = this.formatHash(this.map);
-                       if (this.lastHash != hash) {
-                               location.replace(hash);
-                               this.lastHash = hash;
-                       }
-               },
-
-               movingMap: false,
-               update: function() {
-                       var hash = location.hash;
-                       if (hash === this.lastHash) {
-                               return;
-                       }
-                       var parsed = this.parseHash(hash);
-                       if (parsed) {
-                               this.movingMap = true;
-
-                               this.map.setView(parsed.center, parsed.zoom);
-
-                               this.movingMap = false;
-                       } else {
-                               this.onMapMove(this.map);
-                       }
-               },
-
-               // defer hash change updates every 100ms
-               changeDefer: 100,
-               changeTimeout: null,
-               onHashChange: function() {
-                       // throttle calls to update() so that they only happen every
-                       // `changeDefer` ms
-                       if (!this.changeTimeout) {
-                               var that = this;
-                               this.changeTimeout = setTimeout(function() {
-                                       that.update();
-                                       that.changeTimeout = null;
-                               }, this.changeDefer);
-                       }
-               },
-
-               isListening: false,
-               hashChangeInterval: null,
-               startListening: function() {
-                       this.map.on("moveend", this.onMapMove, this);
-
-                       if (HAS_HASHCHANGE) {
-                               L.DomEvent.addListener(window, "hashchange", this.onHashChange);
-                       } else {
-                               clearInterval(this.hashChangeInterval);
-                               this.hashChangeInterval = setInterval(this.onHashChange, 50);
-                       }
-                       this.isListening = true;
-               },
-
-               stopListening: function() {
-                       this.map.off("moveend", this.onMapMove, this);
-
-                       if (HAS_HASHCHANGE) {
-                               L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
-                       } else {
-                               clearInterval(this.hashChangeInterval);
-                       }
-                       this.isListening = false;
-               }
-       };
-       L.hash = function(map) {
-               return new L.Hash(map);
-       };
-       L.Map.prototype.addHash = function() {
-               this._hash = L.hash(this);
-       };
-       L.Map.prototype.removeHash = function() {
-               this._hash.remove();
-       };
-})(window);