Use our own hash implementation

We need more flexibility than L.Hash provides.
This commit is contained in:
John Firebaugh 2013-11-11 10:27:37 -08:00
parent aa45efc1b7
commit a805e0b545
8 changed files with 71 additions and 199 deletions

View 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

View file

@ -9,7 +9,6 @@
//= require osm
//= require leaflet
//= require leaflet.osm
//= require leaflet.hash
//= require leaflet.map
//= require leaflet.zoom
//= require leaflet.locationfilter

View 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),

View file

@ -140,6 +140,7 @@ L.OSM.layers = function(options) {
} else {
map.removeLayer(layer);
}
map.fire('overlaylayerchange', {layer: layer});
});
map.on('layeradd layerremove', function() {

View 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 %>,

View 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;
},

View 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);
}

View file

@ -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);