// seach a document containing list items <li>, one list item
// per title, and only select those list items that contain text.
// highlight text, remove non matching list items
// Known faults, search uses regular expressions, but highlighting will
// not highlight properly
// Copyright: Stephen Larcombe, Plough Book Sales, April 2003

var searchCount = 0;	// counter of number of searches
function do_search(ss)
{
	// colours to highlight search string with
	// use mostly brighter colours, as black text is used
	var colourlist = ["aqua", "silver", 
		"blue", "lime", "yellow", "fuchsia", "red"];

	if(ss.length == 0)
		return;

	// add a search message node
	var searchmsg = document.createElement("p");
	searchmsg.appendChild(document.createTextNode("Searching for: "));
	var sstring = document.createElement("span");
	sstring.style.color = "black";
	sstring.style.backgroundColor = colourlist[searchCount];
	sstring.appendChild(document.createTextNode(ss));
	searchmsg.appendChild(sstring);
	var node = document.getElementById('searchform');
	node.parentNode.insertBefore(searchmsg,node.nextSibling);

	var match_count = 0;
	var titleCount = 0;	// how many titles found

	traverseTree(document.documentElement,ss.toUpperCase(),colourlist[searchCount]);

	if(match_count > 100)	// if bailed due to too many matches
		searchmsg.appendChild(document.createTextNode(", there has been over 100 matches found in " + titleCount + " titles, searching aborted, please use a different search"));
	else if (match_count == 0)	// no matches
	{
		searchmsg.appendChild(document.createTextNode(", there has been no matches found, you may like to "));
		var newlink = document.createElement("a");
		newlink.setAttribute("href","query.htm");
		newlink.appendChild(document.createTextNode("Search Entire Site"));
		searchmsg.appendChild(newlink);
	}
	else	// if not too many and not too little, display how many titles found
		searchmsg.appendChild(document.createTextNode(", there has been " + titleCount + " matching titles found"));
	// after processing, update the count of the search
	searchCount++;
	searchCount = searchCount % colourlist.length;

	// **************** end of function code *************
	// get all the text that is in a given node, and return it
	function extractNodeText(node)
	{
		var text = "";
		if(node.nodeType == 3) // if node.TEXT_NODE
			//text = node.data
			text = node.nodeValue;
		else	// non text node, recurse
		{
			var children = node.childNodes;
			for(var i = 0; i < children.length; i++)
				text += extractNodeText(children[i]);
		}
		return(text)
	}

	// highlight all locations of the search string in selected titles
	function sshighlight(node,text,spos,ss,colour)
	{
		if(node.nodeType == 3) // if node.TEXT_NODE
		{
			// if location of matching string not here
			if((spos.count + node.nodeValue.length) <= spos.start)
			{
				spos.count += node.nodeValue.length;
			}
			else	// some or all of the search string is in this node
			{
				// adjust search length when highlighting
				spos = dohighlight(node,spos,colour);
				if(spos.length == 0)	// no more to display
				{			// look for new match
					// if no more new matches, return fail
					var start;
					if((start = text.slice(spos.start,text.length).toUpperCase().search(ss)) == -1)
						return(null);
					// else keep searching
					match_count++;	// another match
					if(match_count > 100)
						return(null);
					spos.start += start;
					spos.length = ss.length;
				}
			}
		}
		else	// non text node, recurse
		{
			var children = node.childNodes;
			for(var i = 0; i < children.length; i++)
				if((spos = sshighlight(children[i],text,spos,ss,colour)) == null)	// no more string match
					return(null);
		}
		return(spos);	// got a match
	}

	// given a single node, split it up and create highlighting
	// of ss text at index location
	function dohighlight(node,spos,colour)
	{
		var location = spos.start - spos.count;
		var length = ((location + spos.length) > node.nodeValue.length)
			? node.nodeValue.length - location : spos.length;
		if(location != 0)
		{
			node.parentNode.insertBefore(document.createTextNode(node.nodeValue.slice(0,location)),node);
			// alter size of start
			spos.count += location; // size of new inserted node
		}
		else	// first sting is search string
			spos.count += length;	// seach length
		// assume search string is non zero, this was already checked
		var sstring = document.createElement("span");
		sstring.style.color = "black";
		sstring.style.backgroundColor = colour;
		sstring.appendChild(document.createTextNode(node.nodeValue.slice(location,location+length)));
		node.parentNode.insertBefore(sstring,node);
		// if there is some string left over at end
		if(location+length != node.nodeValue.length)
			node.parentNode.insertBefore(document.createTextNode(node.nodeValue.slice(location+length,node.nodeValue.length)),node);
			
		node.parentNode.removeChild(node);
		spos.length -= length; // search string length reduced
		spos.start += length;	// start of next highlight has moved
		return(spos);
	}

	// return 1 for success, return 0 to fail
	function traverseTree(currentElement,ss,colour)
	{
		var spos = new Object();	// search position object
		// if <li> tag
		if((currentElement.nodeType == 1) &&	/* Node.ELEMENT_TYPE */
		   (currentElement.tagName.length == 2) && (currentElement.tagName == "LI"))
		{
			var text = extractNodeText(currentElement);
			if((spos.start = text.toUpperCase().search(ss)) == -1)	// no match found 
				currentElement.parentNode.removeChild(currentElement);	// remove
			else	// keep match 
			{
				titleCount++;
				match_count++;
				if(match_count > 100)
					return(0);
				spos.count = 0;	// count how far into string
				spos.length = ss.length;
				spos = sshighlight(currentElement,text,spos,ss,colour)
			}
		}
		else	// not list item, so search its sub trees
		{
			var children = currentElement.childNodes;
			// go backwards so none are missed when deleting
			for(var i = children.length-1; i >= 0; i--)
				if(traverseTree(children[i],ss,colour) == 0)
					return(0);
		}
		return(1);
	}
}
