-
Notifications
You must be signed in to change notification settings - Fork 0
/
pageinpost.js
212 lines (174 loc) · 5.12 KB
/
pageinpost.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Wrapped in a function so as to not pollute the global scope.
var activatables = (function () {
// The CSS classes to use for active/inactive elements.
var activeClass = 'active';
var inactiveClass = 'inactive';
var anchors = {}, activates = {};
var regex = /#([A-Za-z][A-Za-z0-9:._-]*)$/;
// Find all anchors (<a href="#something">.)
var temp = document.getElementsByTagName('a');
for (var i = 0; i < temp.length; i++) {
var a = temp[i];
// Make sure the anchor isn't linking to another page.
if ((a.pathname != location.pathname &&
'/' + a.pathname != location.pathname) ||
a.search != location.search) continue;
// Make sure the anchor has a hash part.
var match = regex.exec(a.href);
if (!match) continue;
var id = match[1];
// Add the anchor to a lookup table.
if (id in anchors)
anchors[id].push(a);
else
anchors[id] = [a];
}
// Adds/removes the active/inactive CSS classes depending on whether the
// element is active or not.
function setClass(elem, active) {
var classes = elem.className.split(/\s+/);
var cls = active ? activeClass : inactiveClass, found = false;
for (var i = 0; i < classes.length; i++) {
if (classes[i] == activeClass || classes[i] == inactiveClass) {
if (!found) {
classes[i] = cls;
found = true;
} else {
delete classes[i--];
}
}
}
if (!found) classes.push(cls);
elem.className = classes.join(' ');
}
// Functions for managing the hash.
function getParams() {
var hash = location.hash || '#';
var parts = hash.substring(1).split('&');
var params = {};
for (var i = 0; i < parts.length; i++) {
var nv = parts[i].split('=');
if (!nv[0]) continue;
params[nv[0]] = nv[1] || null;
}
return params;
}
function setParams(params) {
var parts = [];
for (var name in params) {
// One of the following two lines of code must be commented out. Use the
// first to keep empty values in the hash query string; use the second
// to remove them.
//parts.push(params[name] ? name + '=' + params[name] : name);
if (params[name]) parts.push(name + '=' + params[name]);
}
location.hash = knownHash = '#' + parts.join('&');
}
// Looks for changes to the hash.
var knownHash = location.hash;
function pollHash() {
var hash = location.hash;
if (hash != knownHash) {
var params = getParams();
for (var name in params) {
if (!(name in activates)) continue;
activates[name](params[name]);
}
knownHash = hash;
}
}
setInterval(pollHash, 250);
function getParam(name) {
var params = getParams();
return params[name];
}
function setParam(name, value) {
var params = getParams();
params[name] = value;
setParams(params);
}
// If the hash is currently set to something that looks like a single id,
// automatically activate any elements with that id.
var initialId = null;
var match = regex.exec(knownHash);
if (match) {
initialId = match[1];
}
// Takes an array of either element IDs or a hash with the element ID as the key
// and an array of sub-element IDs as the value.
// When activating these sub-elements, all parent elements will also be
// activated in the process.
function makeActivatable(paramName, activatables) {
var all = {}, first = initialId;
// Activates all elements for a specific id (and inactivates the others.)
function activate(id) {
if (!(id in all)) return false;
for (var cur in all) {
if (cur == id) continue;
for (var i = 0; i < all[cur].length; i++) {
setClass(all[cur][i], false);
}
}
for (var i = 0; i < all[id].length; i++) {
setClass(all[id][i], true);
}
setParam(paramName, id);
return true;
}
activates[paramName] = activate;
function attach(item, basePath) {
if (item instanceof Array) {
for (var i = 0; i < item.length; i++) {
attach(item[i], basePath);
}
} else if (typeof item == 'object') {
for (var p in item) {
var path = attach(p, basePath);
attach(item[p], path);
}
} else if (typeof item == 'string') {
var path = basePath ? basePath.slice(0) : [];
var e = document.getElementById(item);
if (!e) throw 'Could not find "' + item + '".'
path.push(e);
if (!first) first = item;
// Store the elements in a lookup table.
all[item] = path;
// Attach a function that will activate the appropriate element
// to all anchors.
if (item in anchors) {
// Create a function that will call the 'activate' function with
// the proper parameters. It will be used as the event callback.
var func = (function (id) {
return function (e) {
activate(id);
if (!e) e = window.event;
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
return false;
};
})(item);
for (var i = 0; i < anchors[item].length; i++) {
var a = anchors[item][i];
if (a.addEventListener) {
a.addEventListener('click', func, false);
} else if (a.attachEvent) {
a.attachEvent('onclick', func);
} else {
throw 'Unsupported event model.';
}
all[item].push(a);
}
}
return path;
} else {
throw 'Unexpected type.';
}
return basePath;
}
attach(activatables);
// Activate an element.
if (first) activate(getParam(paramName)) || activate(first);
}
return makeActivatable;
})();