diff --git a/js/id/renderer/hash.js b/js/id/renderer/hash.js
index 67138539e..4365a1515 100644
--- a/js/id/renderer/hash.js
+++ b/js/id/renderer/hash.js
@@ -2,7 +2,6 @@ iD.Hash = function() {
var hash = {},
s0, // cached location.hash
lat = 90 - 1e-8, // allowable latitude range
- hadHash,
map;
function qs(str) {
@@ -40,7 +39,7 @@ iD.Hash = function() {
function hashchange() {
if (location.hash === s0) return; // ignore spurious hashchange events
- if (parser(map, (s0 = location.hash).substring(1)))
+ if (parser(map, (s0 = location.hash).substring(2)))
move(); // replace bogus hash
}
@@ -56,13 +55,11 @@ iD.Hash = function() {
window.addEventListener("hashchange", hashchange, false);
if (location.hash) {
hashchange();
- hadHash = true;
+ hash.hadHash = true;
}
}
return hash;
};
- hash.hadHash = hadHash;
-
return hash;
};
diff --git a/test/index.html b/test/index.html
index 7a13f27e8..9d7da2254 100644
--- a/test/index.html
+++ b/test/index.html
@@ -48,6 +48,7 @@
+
diff --git a/test/spec/hash.js b/test/spec/hash.js
new file mode 100644
index 000000000..30ab83d76
--- /dev/null
+++ b/test/spec/hash.js
@@ -0,0 +1,97 @@
+describe("hash", function () {
+ var hash, map;
+
+ beforeEach(function () {
+ hash = iD.Hash();
+ map = {
+ on: function () { return map; },
+ off: function () { return map; },
+ zoom: function () { return arguments.length ? map : 0; },
+ center: function () { return arguments.length ? map : [0, 0] }
+ };
+ });
+
+ afterEach(function () {
+ hash.map(null);
+ location.hash = "";
+ });
+
+ describe("#map()", function () {
+ it("gets and sets map", function () {
+ expect(hash.map(map)).toBe(hash);
+ expect(hash.map()).toBe(map);
+ });
+
+ it("sets hadHash if location.hash is present", function () {
+ location.hash = "?map=20.00/38.87952/-77.02405";
+ hash.map(map);
+ expect(hash.hadHash).toBeTruthy();
+ });
+
+ it("zooms map to requested level", function () {
+ location.hash = "?map=20.00/38.87952/-77.02405";
+ spyOn(map, 'zoom').andCallThrough();
+ hash.map(map);
+ expect(map.zoom).toHaveBeenCalledWith(20.0);
+ });
+
+ it("centers map at requested coordinates", function () {
+ location.hash = "?map=20.00/38.87952/-77.02405";
+ spyOn(map, 'center').andCallThrough();
+ hash.map(map);
+ expect(map.center).toHaveBeenCalledWith([-77.02405, 38.87952]);
+ });
+
+ it("binds the map's move event", function () {
+ spyOn(map, 'on');
+ hash.map(map);
+ expect(map.on).toHaveBeenCalledWith('move', jasmine.any(Function));
+ });
+
+ it("unbinds the map's move event", function () {
+ spyOn(map, 'on');
+ spyOn(map, 'off');
+ hash.map(map);
+ hash.map(null);
+ expect(map.off).toHaveBeenCalledWith('move', map.on.mostRecentCall.args[1]);
+ });
+ });
+
+ describe("on window hashchange events", function () {
+ var hashchanged,
+ hashchange = function () { return hashchanged; };
+
+ beforeEach(function() {
+ hash.map(map);
+ hashchanged = false;
+ window.addEventListener("hashchange", function () { hashchanged = true; }, false);
+ });
+
+ it("zooms map to requested level", function () {
+ spyOn(map, 'zoom').andCallThrough();
+ location.hash = "#?map=20.00/38.87952/-77.02405";
+ waitsFor(hashchange);
+ runs(function () {
+ expect(map.zoom).toHaveBeenCalledWith(20.0);
+ });
+ });
+
+ it("centers map at requested coordinates", function () {
+ spyOn(map, 'center').andCallThrough();
+ location.hash = "#?map=20.00/38.87952/-77.02405";
+ waitsFor(hashchange);
+ runs(function () {
+ expect(map.center).toHaveBeenCalledWith([-77.02405, 38.87952]);
+ });
+ });
+ });
+
+ describe("on map move events", function () {
+ it("stores the current zoom and coordinates in location.hash", function () {
+ spyOn(map, 'on');
+ hash.map(map);
+ map.on.mostRecentCall.args[1]();
+ expect(location.hash).toEqual("#?map=0.00/0/0");
+ });
+ });
+});