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