/
url-state.js
168 lines (140 loc) · 4.55 KB
/
url-state.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
var selected_marker = null;
var selected_icon = 0;
// There are four bits of state:
// 1. Selected date range
// 2. Selected dot
// 3. Expanded image
// 4. Map position & zoom
function currentState() {
var years = $('#slider').slider('values');
if (years[0] == 1850 && years[1] == 2000) years = null;
var selected_lat_lon = selected_marker ? selected_marker.title : null;
if (selected_lat_lon == init_lat_lon) selected_lat_lon = null;
var expanded = null;
if ($('#expanded').css('display') != 'none') {
var photo_id = $('.current').attr('photo_id');
var width = $('.current img').width();
expanded = photo_id + ',' + width;
}
var center = map.getCenter();
var map_state = center.lat().toFixed(5) + ',' + center.lng().toFixed(5) + ',' + map.getZoom();
if (map_state == '37.79216,-122.41753,14') map_state = null;
var state = {};
if (years) state.y = years[0] + '-' + years[1];
if (selected_lat_lon) state.ll = selected_lat_lon;
if (expanded) state.e = expanded;
if (map_state) state.m = map_state;
return state;
}
// Converts a state dictionary to something that can go in the URL hash.
// Returned string does not include leading '#'.
function stateToHash(state) {
var hash = '';
for (var k in state) {
if (hash) hash += '&';
hash += k + ':' + state[k].replace(/,/g, '|');
}
return hash;
}
// Inverse of the above.
function hashToState(url_hash) {
if (!url_hash) return {};
var hash = '' + url_hash; // in case location.hash is passed in.
if (hash.substr(0, 1) == '#') {
hash = hash.substr(1);
}
if (hash.indexOf('%7') >= 0) {
// twitter links come through as 'foo%7Cbar', not 'foo|bar'.
hash = unescape(hash);
}
var parts = hash.split(/&|,/);
var state = {};
for (var i = 0; i < parts.length; i++) {
var kv = parts[i].split(':');
var v = kv[1];
if (v.indexOf('|') != -1) v = v.replace(/\|/g, ',');
state[kv[0]] = v;
}
return state;
}
function areStatesEqual(state1, state2) {
// I'm lazy.
return JSON.stringify(state1) == JSON.stringify(state2);
}
var block_update = false; // used when loading from a hash
var current_state = null; // this is a state dictionary
// Call this whenever something happens to change the state of the page.
function stateWasChanged() {
if (block_update) return;
var old_state = current_state;
current_state = currentState();
var hash = stateToHash(current_state);
// Only save a state to the browser history when the selected dot changes.
var saveToHistory = (!old_state || old_state.ll != current_state.ll);
block_update = true;
setUrlHash(hash, saveToHistory);
block_update = false;
}
// Make the UI match the state object.
function loadFromState(state) {
block_update = true;
if (state.hasOwnProperty('m')) {
var m = state['m'].split(',');
var ll = new google.maps.LatLng(parseFloat(m[0]), parseFloat(m[1]));
var zoom = parseInt(m[2]);
map.setCenter(ll);
map.setZoom(parseInt(zoom));
}
if (state.hasOwnProperty('y')) {
var ys = state['y'].split('-');
ys = [parseInt(ys[0]), parseInt(ys[1])];
$('#slider').slider('values', ys);
slide();
}
var expanded_photo_id = null;
if (state.hasOwnProperty('e')) {
// &e=photo_id,expanded_photo_width
var e = state['e'].split(',');
expanded_photo_id = e[0];
var w = parseInt(e[1]); // used to be useful, but no longer necessary.
// We can only call showExpanded once the /info request completes.
// This gets done below for '&ll=', so we piggyback on that call.
}
if (state.hasOwnProperty('ll')) {
var marker = null;
for (var i = 0; i < markers.length; i++) {
if (markers[i].title == state.ll) {
marker = markers[i];
break;
}
}
if (marker) {
displayInfoForLatLon(state.ll, marker, function() {
if (expanded_photo_id) {
showExpanded(expanded_photo_id);
}
});
}
}
block_update = false;
}
// Updates the URL hash, optionally including this URL in the browser's
// navigation history.
// |hash| should not include the leading '#'.
function setUrlHash(hash, include_in_history) {
// Only clicks on new dots should enter the navigation history.
if (include_in_history) {
location.assign('#' + hash);
} else {
location.replace('#' + hash);
}
}
// Updates the UI based on the current URL hash.
function setUIFromUrlHash() {
if (block_update) return;
var state = hashToState(location.hash);
if (areStatesEqual(state, current_state)) return;
loadFromState(state);
}
// This enables pasting hashed URLs
$(window).hashchange(setUIFromUrlHash);