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
|
/*
* Expandable list implementation.
* by David Lindquist <first name><at><last name><dot><net>
* See:
* http://www.gazingus.org/html/DOM-Scripted_Lists_Revisited.html
* Modifies lists so that sublists can be hidden and shown by means of
* a switch. The switch is a node inserted into the DOM tree as the
* first child of the list item containing the sublist.
*/
// The script will only be applied to lists containing this class name,
// e.g.: <ul class="foo expandable">...</ul>.
var CLASS_NAME = "expandable";
// This value is the assumed initial display style for a sublist when it cannot
// be determined by other means. See below.
var DEFAULT_DISPLAY = "none";
// The namespace to use when using this script in an XML context.
var XMLNS = "http://www.w3.org/1999/xhtml";
// The beginning of the title text for the switch when the sublist is collapsed.
var CLOSED_PREFIX = "Expand list: ";
// The beginning of the title text for the switch when the sublist is expanded.
var OPENED_PREFIX = "Collapse list: ";
/******************************************************************************/
// Returns a copy of a string with leading and trailing whitespace removed.
String.prototype.trim = function() {
return this.replace(/^\s+/, "").replace(/\s+$/, "");
}
// Walks the DOM tree starting at a given root element. Returns an
// array of nodes of the specified type and conforming to the criteria
// of the given filter function. The filter should return a boolean.
function getNodesByType(root, type, filter) {
var node = root;
var nodes = [];
var next;
while (node != null) {
if (node.hasChildNodes())
node = node.firstChild;
else if (node != root && null != (next = node.nextSibling))
node = next;
else {
next = null;
for ( ; node != root; node = node.parentNode) {
next = node.nextSibling;
if (next != null) break;
}
node = next;
}
if (node != null && node.nodeType == type && filter(node))
nodes.push(node);
}
return nodes;
}
// Simulates the innerText property of IE and other browsers.
// Mozilla/Firefox need this.
function getInnerText(node) {
if (node == null || node.nodeType != 1)
return;
var text = "";
var textnodes = getNodesByType(node, 3, function() { return true; });
for (var i = 0; i < textnodes.length; i++)
text += textnodes[i].data;
return text;
}
function initExpandableLists() {
if (!document.getElementsByTagName) return;
// Top-level function to accommodate browsers that do not register
// a click event when a link is activated by the keyboard.
switchNode = function(id) {
var node = document.getElementById(id);
if (node && /^switch /.test(node.className)) node.onclick();
}
// Top-level function to be assigned as the event handler for the
// switch. This could have been bound to the handler as a closure,
// but closures are associated with memory leak problems in IE.
actuate = function() {
var sublist = this.parentNode.getElementsByTagName("ul")[0] ||
this.parentNode.getElementsByTagName("ol")[0];
if (sublist.style.display == "block") {
sublist.style.display = "none";
this.firstChild.data = "+";
this.className = "switch off";
this.title = this.title.replace(OPENED_PREFIX, CLOSED_PREFIX);
} else {
sublist.style.display = "block";
this.firstChild.data = "-";
this.className = "switch on";
this.title = this.title.replace(CLOSED_PREFIX, OPENED_PREFIX);
}
return false;
}
// Create switch node from which the others will be cloned.
if (typeof document.createElementNS == "function")
var template = document.createElementNS(XMLNS, "a");
else
var template = document.createElement("a");
template.appendChild(document.createTextNode(" "));
var list, i = 0, j = 0;
var pattern = new RegExp("(^| )" + CLASS_NAME + "( |$)");
while ((list = document.getElementsByTagName("ul")[i++]) ||
(list = document.getElementsByTagName("ol")[j++]))
{
// Only lists with the given class name are processed.
if (pattern.test(list.className) == false) continue;
var item, k = 0;
while ((item = list.getElementsByTagName("li")[k++])) {
var sublist = item.getElementsByTagName("ul")[0] ||
item.getElementsByTagName("ol")[0];
// No sublist under this list item. Skip it.
if (sublist == null) continue;
// Attempt to determine initial display style of the
// sublist so the proper symbol is used.
var symbol;
switch (sublist.style.display) {
case "none" : symbol = "+"; break;
case "block": symbol = "-"; break;
default:
var display = DEFAULT_DISPLAY;
if (sublist.currentStyle) {
display = sublist.currentStyle.display;
} else if (document.defaultView &&
document.defaultView.getComputedStyle &&
document.defaultView.getComputedStyle(sublist, ""))
{
var view = document.defaultView;
var computed = view.getComputedStyle(sublist, "");
display = computed.getPropertyValue("display");
}
symbol = (display == "none") ? "+" : "-";
// Explicitly set the display style to make sure it is
// set for the next read. If it is somehow the empty
// string, use the default value from the (X)HTML DTD.
sublist.style.display = display || "block";
break;
}
// This bit attempts to extract some text from the first
// child node of the list item to append to the title
// attribute of the switch.
var child = item.firstChild;
var text = "";
while (child) {
if (child.nodeType == 3 && "" != child.data.trim()) {
text = child.data;
break;
} else if (child.nodeType == 1 &&
!/^[ou]l$/i.test(child.tagName))
{
text = child.innerText || getInnerText(child);
break;
}
child = child.nextSibling;
}
var actuator = template.cloneNode(true);
// a reasonably unique ID
var uid = "switch" + i + "-" + j + "-" + k;
actuator.id = uid;
actuator.href = "javascript:switchNode('" + uid + "')";
actuator.className = "switch " + ((symbol == "+") ? "off" : "on");
actuator.title = ((symbol == "+")
? CLOSED_PREFIX : OPENED_PREFIX) + text.trim();
actuator.firstChild.data = symbol;
actuator.onclick = actuate;
item.insertBefore(actuator, item.firstChild);
}
}
}
// Props to Simon Willison:
// http://simon.incutio.com/archive/2004/05/26/addLoadEvent
var oldhandler = window.onload;
window.onload = (typeof oldhandler == "function")
? function() { oldhandler(); initExpandableLists(); } : initExpandableLists;
|