function toggleCollection(val, collect) {
	if (collect.length > 0) {
		for (var i=0; i<collect.length; i++) {
			var curr = collect[i];
			if (curr.nodeType == Node.ELEMENT_NODE) {
				curr.setAttribute("class", val);
			}
		}
	}
}

function showCollection(collect) {
	toggleCollection("trunc_visible", collect);
}

function hideCollection(collect) {
	toggleCollection("trunc_hidden", collect);
}

function showMore(self, collect) {
	showCollection(collect);
	var theLink = document.getElementById(self);
	if (theLink) {
		theParent = theLink.parentNode;
		theParent.removeChild(theLink);
	}
}

function createMoreLink(colName) {
	var theMoreLink = document.createElement("a");
	var theLinkIdent = colName + "_link";
	theMoreLink.setAttribute("id", theLinkIdent);
	theMoreLink.setAttribute("name", theLinkIdent);
	theMoreLink.setAttribute("href", "#"+theLinkIdent);
	theMoreLink.setAttribute("onclick", "showMore('" + theLinkIdent + "', " + colName + ")");
	theMoreLink.appendChild(document.createTextNode(" more..."));
	return theMoreLink;
}

function appendMoreLink(par, colName) {
	var theMoreLink = createMoreLink(colName);
	par.appendChild(theMoreLink);
/* 	var theMoreLink = document.createElement("a"); */
/* 	var theLinkIdent = colName + "_link"; */
/* 	theMoreLink.setAttribute("id", theLinkIdent); */
/* 	theMoreLink.setAttribute("name", theLinkIdent); */
/* 	theMoreLink.setAttribute("href", "#"+theLinkIdent); */
/* 	theMoreLink.setAttribute("onclick", "showMore('" + theLinkIdent + "', " + colName + ")"); */
/* 	theMoreLink.appendChild(document.createTextNode(" more...")); */
/* 	if (par.hasChildNodes()) { */
/* 		par.lastChild.appendChild(theMoreLink); */
/* 	} else { */
/* 	par.appendChild(theMoreLink); */
/* 	} */
}

function hideIfNecessary(ndname, thresh, collect, colname) {
	var seen = 0;
	var theNode = document.getElementById(ndname);
	if (theNode) {
		theNode.normalize();
		seen = walkThresh(theNode, thresh, 0, collect);
		if (seen == thresh) {
			if (collect.length > 0) {
				var theFirstHidden = collect[0];
				var theParent = theFirstHidden.parentNode;
				theParent.insertBefore(createMoreLink(colname), theFirstHidden);
			} else {
				appendMoreLink(theNode, colname);
			}
		}
	}
}

function walkAll(start) {
	if (start.hasChildNodes()) {
		start.normalize(); 	// off the bat, compact the full subtree
							// (this way, we can assume that regions which produce 
							// textual output are not contiguous... text whose
							// parent node contains only text have no siblings)
	}
	return walk(start);
}

// whitespace is {'\n', '\r', '\t', ' '}
function walk(node) {
	var haveSeen = 0;
	if (node) {
/* 		debugWrite("Visiting node: " + node.nodeName + " with children: " + node.childNodes.length); */
		if (node.nodeType == Node.TEXT_NODE) {
			var isSpaceSeq = true; // treat init word boundary like whitespace
			var content = node.nodeValue;
/* 			debugWrite("Processing text: " + content); */
			for(var pos=0; pos < content.length; pos++) {
				if (isWhitespace(content.charAt(pos))) {
					if (!isSpaceSeq) { // not already looking at a run of whitespace
						isSpaceSeq = true; // now we are
					} // otherwise, not a new word boundary
				} else { // in word
					if (isSpaceSeq) { // last read char was whitespace, so word-bound
						haveSeen++;
						isSpaceSeq = false;
					}
				}
			}
		} else if (node.nodeType == Node.ELEMENT_NODE) {
			if (node.hasChildNodes()) {
/* 				debugWrite("Visiting children of " + node.nodeName); */
				for (var iter=0; iter < node.childNodes.length; iter++) {
					var theChild = node.childNodes.item(iter);
/* 					debugWrite("Processing child " + iter + " of " + node.nodeName + " (type: " + theChild.nodeType + ")"); */
					haveSeen += walk(theChild);
				}
			}
/* 			debugWrite("Total in children: " + childrenSeen); */
		}
	}
	return haveSeen;
}

function walkThresh(nd, thresh, seen, collect) {
	var seenHere = seen;
	if (nd) {
		if (nd.nodeType == Node.TEXT_NODE) {
			// if we're not up to threshold
			var pos = 0; // init position in content string
			var isSpaces = true; // boundary condition
			var content = nd.nodeValue;
			while (seenHere < thresh && pos < content.length) { 
				if (isWhitespace(content.charAt(pos))) {
					if (!isSpaces) { // if we weren't in a run of spaces before...
						isSpaces = true; // ... then we are now
					}
				} else {
					if (isSpaces) { // last char was whitespace, cur char isn't -> word boundary
						seenHere++;
						isSpaces = false; // not in space-run anymore
					}
				}
				pos++;
			}
			if (pos + 1 < content.length) { // partial content to split
				var firstPart = content.substring(0, pos-1);
				var remainder = content.substring(pos-1, content.length);
				var nonhiddenText = document.createTextNode(firstPart);
				var hiddenText = document.createTextNode(remainder);
				var theParent = nd.parentNode;
				var hideContainer = document.createElement("span");
				hideContainer.setAttribute("class", "trunc_hidden");
				hideContainer.appendChild(hiddenText);
				theParent.removeChild(nd);
				theParent.appendChild(nonhiddenText);
				theParent.appendChild(hideContainer);
				collect.push(hideContainer);
			}
		} else if (nd.nodeType == Node.ELEMENT_NODE) {
			if (nd.hasChildNodes()) {
				for (var iter=0; iter < nd.childNodes.length; iter++) {
					var theChild = nd.childNodes.item(iter);
					if (seenHere < thresh) {
						seenHere = walkThresh(theChild, thresh, seenHere, collect);
					} else {
						if (theChild.nodeType == Node.ELEMENT_NODE) {
							theChild.setAttribute("class", "trunc_hidden");
							collect.push(theChild);
						} else {
							// child node isn't element, so put into span which can be hidden
							var hideContainer = document.createElement("span");
							hideContainer.setAttribute("class", "trunc_hidden");
							hideContainer.appendChild(theChild.cloneNode(true));
							nd.replaceChild(hideContainer, theChild);
							collect.push(hideContainer);
						}
					}
				}
			} else {
				if (seenHere >= thresh) {
					nd.setAttribute("class", "trunc_hidden");
					collect.push(nd);
				}
			}
		} else {
//			debugWrite("(Unexpectedly) parsing unidentified node (" + nd.toString() + ")");
		}			
	}
	return seenHere;
}
	
function isWhitespace(ch) {
	return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t');
}

function debugWrite(str) {
	var debugRegion = document.getElementById("debug");
	if (debugRegion) {
		var newStmt = document.createElement("div");
		newStmt.setAttribute("class", "stmt");
		newStmt.appendChild(document.createTextNode(str));
		debugRegion.appendChild(newStmt);
	} else {
		alert("Could not find debugging region to record message: " + str);
	}
}

function countWalk(node) {
	var total = 1;
	if (node == null) {
		return 0;
	} else if (node.hasChildNodes()) {
		debugWrite("Nodes: " + node.childNodes.length)
		total = 0;
		for(i=0; i<node.childNodes.length; i++) {
			debugWrite("Nodes: " + node.childNodes.item(i).nodeType);
			total = total + countWalk(node.childNodes.item(i));
		}
		
/* 		for (curr = node.firstChild; curr != null; curr = node.nextSibling) { */
/* //			total += countWalk(curr); */
/* 			debugWrite("Walking over: " + curr.nodeName); */
/* 			total++; */
/* 		} */
		return total;
	} else {
		return 1;
	}
}
