// _DocBrowser.js
//
// Script code used by DocBrowser frameset files.
//

/*---------- Root Frameset Script ----------*/

// globals
g_fAutoSyncDone = false; // true after "Sync to TOC" due to "#topic.htm"

function OnTopicLoaded(idTopic, idOverloadListTopic)
{
	// called from within a topic file (right-hand frame) when it loads;
	// <idTopic> is the numeric ID of the topic that loaded;
	// <idOverloadListTopic> is the numeric ID of the topic's related overload
	// list topic or null if none...

	// do nothing if the frames aren't fully loaded yet
	if ((window.dbc_OnTopicLoaded == undefined) ||
	    (window.dbi_OnTopicLoaded == undefined) ||
	    (window.dbt_SyncTOC == undefined))
		return;

	// notify *_Contents.htm
	window.dbc_OnTopicLoaded(idTopic, idOverloadListTopic);

	// notify *_Index.htm
	window.dbi_OnTopicLoaded(idTopic, idOverloadListTopic);

	// if "#topic.htm" is specified in the URL, do an automatic "Sync to TOC"
	try
	{
		var strBookmark = location.hash;
	}
	catch (e)
	{
		var strBookmark = "";
	}
	if ((strBookmark.length > 0) && !g_fAutoSyncDone)
	{
		window.dbt_SyncTOC();
		g_fAutoSyncDone = true;
	}
}

function DB_DocTopic_OnLoad()
{
	// called when a topic loads into the topic frame (right-hand frame);
	// if that file doesn't implement OnTopicLoaded() (e.g. the file is static,
	// not auto-generated by CodeDoc), the file's name will be looked up in
	// a table to attempt to find its numeric topic ID...
	try
	{
		var strFileName = DocTopic.location.href
			.replace(/.*\//, "").replace(/#.*/, "");
		if (window.dbc_TopicMap != undefined)
		{
			var idTopic = dbc_TopicMap[strFileName];
			if (idTopic != undefined)
				OnTopicLoaded(idTopic, null);
		}
	}
	catch (e)
	{
		// ignore error, e.g. access denied reading <href>
	}
}

/*---------- *_Tabs.htm Script ----------*/

// globals
dbt_atdSelectedTab = null; // array of three cells of selected tab
dbt_strFind = ""; // current "Look for" string

function DBT_OnLoad()
{
	parent.dbt_SyncTOC = DBT_SyncTOC;
	DBT_SelectContentsTab(); // select initial tab
}

function DBT_DisplayTab(atdTab)
{
	if (dbt_atdSelectedTab != null)
	{
		for (var i = 0; i < 3; i++)
		{
			var s = dbt_atdSelectedTab[i].style;
			s.backgroundColor = "threedface";
			s.borderBottom = "1px solid windowframe";
		}
	}

	dbt_atdSelectedTab = atdTab;

	if (atdTab != null)
	{
		for (var i = 0; i < 3; i++)
		{
			var s = atdTab[i].style;
			s.backgroundColor = "window";
			s.borderBottom = "none";
		}
	}
}

function DBT_SelectContentsTab()
{
	DBT_DisplayTab(new Array(ContentsTabLeft, ContentsTab, ContentsTabRight));
	parent.LeftFrameset.rows = "23,*,0";
	FindUi.style.display = "none";
	if (parent.ContentsFrame.window.DBC_SetFocus != undefined)
		parent.ContentsFrame.window.DBC_SetFocus();
	//document.body.onfocus = "";
}

function DBT_SelectIndexTab()
{
	DBT_DisplayTab(new Array(IndexTabLeft, IndexTab, IndexTabRight));
	parent.LeftFrameset.rows = "60,0,*";
	FindUi.style.display = "block";
	setTimeout("Find.focus(); Find.select();", 1);
	if (parent.IndexFrame.window.DBI_InitTopicArray != undefined)
		setTimeout(parent.IndexFrame.window.DBI_InitTopicArray, 1);
}

function DBT_SyncTOC()
{
	// make the table of contents display the page that's currently loaded
	DBT_SelectContentsTab();
	if (parent.ContentsFrame.window.DBC_SelectLoadedTopic != undefined)
		parent.ContentsFrame.window.DBC_SelectLoadedTopic();
}

function DBT_Maximize()
{
	// maximize the topic page, i.e. make it open without a frameset
	try
	{
		parent.navigate(parent.DocTopic.location.href);
	}
	catch (e)
	{
	}
}

function DBT_OnFocus()
{
	Find.focus();
}

function DBT_OnKeyDown()
{
	setTimeout(DBT_Find, 1);
}

function DBT_OnKeyPress()
{
	if (event.keyCode == 13)
		setTimeout(DBT_LoadFoundTopic, 1);
}

function DBT_OnPaste()
{
	setTimeout(DBT_Find, 1);
}

function DBT_Find()
{
	// if the Index page is showing, look up the word the user typed
	if (FindUi.style.display != "block")
		return; // Index not showing
	var strFind = Find.value.replace(/^\s*/, "").replace(/\s*$/, "")
		.replace(/ +/g, " ").toLowerCase();
	if (dbt_strFind != strFind)
	{
		dbt_strFind = strFind;
		if (parent.IndexFrame.window.DBI_Find != undefined)
			parent.IndexFrame.window.DBI_Find(strFind);
	}
}

function DBT_LoadFoundTopic()
{
	// load the scrolled-to topic in the index into the right pane
	if (parent.IndexFrame.window.DBI_LoadSelectedTopic != undefined)
		parent.IndexFrame.window.DBI_LoadSelectedTopic();
}

function DBT_SetFindTextBox(strFind)
{
	// stores <strFind> into the Find text box
	dbt_strFind = strFind;
	Find.value = dbt_strFind;
	Find.select();
}

/*---------- *_Contents.htm Script ----------*/

// globals
dbc_idTopicLoaded = null; // topic ID (e.g. "12") of topic loaded in right pane
dbc_idTopicSelected = null; // topic ID of topic that user selected
dbc_strIncrementalSearch = ""; // current incremental search string
dbc_timeoutIncrementalSearch = null; // timeout that clears search string

function DBC_OnLoad()
{
	parent.dbc_OnTopicLoaded = DBC_OnTopicLoaded;
	parent.dbc_TopicMap = dbc_TopicMap;
	var strHash = parent.location.hash;
	if (strHash.length == 0)
		DBC_SelectFirstTopic(true);
	else
	{
		// load the topic whose file name is specified as a bookmark on the
		// frameset page (e.g. "#whatever.htm")
		var strBookmark = strHash.substr(1).replace(/@/g, "#");
		if (strBookmark.indexOf(":") < 0)
			parent.DocTopic.location.href = strBookmark;
	}
	//window.focus();
}

function DBC_SetFocus()
{
	// set focus to this window
	setTimeout("try { document.body.focus(); } catch (e) { }", 1);
}

function DBC_OnTopicLoaded(idTopic, idOverloadListTopic)
{
	// a topic (e.g. "12") was loaded in the right pane -- update TOC UI;
	// if the topic has an associated overload list topic, use that one
	if (idOverloadListTopic != null)
		idTopic = idOverloadListTopic;
	if (dbc_idTopicLoaded != null)
	{
		var spanTopic = DBC_GetTopicSpan(dbc_idTopicLoaded);
		if (spanTopic != null) // if topic exists in Contents
			spanTopic.style.border = "solid 1px window";
	}
	dbc_idTopicLoaded = idTopic;
	if (dbc_idTopicLoaded != null)
	{
		var spanTopic = DBC_GetTopicSpan(dbc_idTopicLoaded);
		if (spanTopic != null) // if topic exists in Contents
			spanTopic.style.border = "solid 1px threedshadow";
	}
}

function DBC_SelectTopic(idTopic, fLoad)
{
	// select a topic given its ID (e.g. "12"); load it if <fLoad> is true;
	// do nothing if <idTopic> doesn't refer to an existing topic
	if (dbc_idTopicSelected != null)
	{
		// deselect old topic TOC entry
		var spanTopic = DBC_GetTopicSpan(dbc_idTopicSelected);
		if (spanTopic == null)
			return; // no such topic in Contents
		var s = spanTopic.style;
		s.color = "windowtext";
		s.backgroundColor = "window";
	}
	dbc_idTopicSelected = idTopic;
	if (dbc_idTopicSelected != null)
	{
		// select new topic TOC entry
		var divTopic = DBC_GetTopicDiv(dbc_idTopicSelected);
		if (divTopic == null)
			return; // no such topic in Contents
		var s = DBC_GetTopicSpanFromDiv(divTopic).style;
		s.color = "captiontext";
		s.backgroundColor = "activecaption";

		// load topic page (if specified)
		if (fLoad)
		{
			try
			{
				parent.DocTopic.location.href = divTopic.Topic;
			}
			catch (e)
			{
			}
		}

		// make topic visible; expand nodes up to the root
		var divSubtree = divTopic.parentElement;
		while (divSubtree != null)
		{
			var idTopic = DBC_GetSubtreeId(divSubtree);
			if (idTopic == null)
				break; // reached root
			if (divSubtree.style.display.length > 0)
				break; // subtree already expanded
			divTopic = DBC_GetTopicDiv(idTopic);
			divSubtree.style.display = "block";
			DBC_GetTopicImgFromDiv(divTopic).src = "_Opened.gif";
			divSubtree = divSubtree.parentElement
		}
		DBC_ScrollSelectedTopicIntoView();
	}
}

function DBC_OnMouseDown()
{
	// set <elSrc> to the clicked-on element
	var elSrc = event.srcElement;

	// set <divTopic> to the nearest enclosing DIV with a Topic attribute
	var divTopic = elSrc;
	while (true)
	{
		if (divTopic == null)
			return; // user didn't click on a TOC line
		if (divTopic.Topic != undefined)
			break;
		divTopic = divTopic.parentElement;
	}

	// set <idTopic> to the topic ID of the DIV (e.g. "12")
	var idTopic = DBC_GetTopicId(divTopic);

	// process the mouse-down event
	if (elSrc.tagName == "IMG")
	{
		// user clicked icon -- expand/collapse subtree
		var divSubtree = document.all["Subtree" + idTopic];
		if (divSubtree == null)
			return; // no subtree
		if (divSubtree.style.display.length == 0)
		{
			// subtree is closed -- open it
			divSubtree.style.display = "block";
			elSrc.src = "_Opened.gif";
		}
		else
		{
			// subtree is opened -- close it
			divSubtree.style.display = "";
			elSrc.src = "_Closed.gif";
		}
	}
	else
	{
		// user is selecting a page to display
		DBC_SelectTopic(idTopic, true);
	}
}

function DBC_OnMouseOver()
{
	// if the mouse is hovering over a topic label that's partially scrolled
	// off the screen, display its label as a tooltip
	var elSrc = event.srcElement;
	if (elSrc.tagName == "SPAN")
	{
		var idTopic = DBC_GetTopicIdFromSpan(elSrc);
		if (idTopic == null)
			return; // not a topic SPAN
		if (DBC_IsTopicInView(idTopic))
			elSrc.title = ""; // full topic label already in view -- no tooltip
		else
			elSrc.title = elSrc.innerText; // label partially hidden
	}
}

function DBC_OnKeyDown()
{
	switch (event.keyCode)
	{

	case 38: // up-arrow

		DBC_SelectPreviousVisibleTopic()
		return false;

	case 40: // down-arrow

		DBC_SelectNextVisibleTopic()
		return false;

	case 37: // left-arrow

		DBC_Ascend();
		return false;

	case 39: // right-arrow

		DBC_Descend();
		return false;

	case 33: // Page Up

		DBC_PageUp();
		return false;

	case 34: // Page Down

		DBC_PageDown();
		return false;

	case 36: // Home

		DBC_SelectFirstTopic(false);
		return false;

	case 35: // End

		DBC_SelectLastVisibleTopic(false);
		return false;

	case 13: // Enter

		DBC_SelectTopic(dbc_idTopicSelected, true);
		parent.DocTopic.window.focus();
		return false;

	}
}

function DBC_OnKeyPress()
{
	// do incremental search
	if (dbc_idTopicSelected == null)
		return; // no topic selected
	var idTopic = dbc_idTopicSelected;
	if (event.keyCode < 32)
		return; // ignore control characters
	dbc_strIncrementalSearch += String.fromCharCode(event.keyCode);
	if (dbc_timeoutIncrementalSearch != null)
		clearTimeout(dbc_timeoutIncrementalSearch);
	dbc_timeoutIncrementalSearch = setTimeout(DBC_CancelIncrementalSearch, 700);
	if (dbc_strIncrementalSearch.length == 1)
		idTopic = DBC_GetNextVisibleTopic(idTopic);
	while (idTopic != null)
	{
		var strLabel = DBC_GetTopicSpan(idTopic).innerText;
		if (StartsWithIC(strLabel, dbc_strIncrementalSearch))
		{
			DBC_SelectTopic(idTopic, false);
			break;
		}
		idTopic = DBC_GetNextVisibleTopic(idTopic);
	}
	return false;
}

function StartsWithIC(str, strPrefix)
{
	// returns true if <str> begins with <strPrefix>, ignoring case
	return str.substr(0, strPrefix.length).toLowerCase() ==
		strPrefix.toLowerCase();
}

function DBC_CancelIncrementalSearch()
{
	// called after a short interval of inactivity during incremental search
	dbc_strIncrementalSearch = "";
	if (dbc_timeoutIncrementalSearch != null)
	{
		clearTimeout(dbc_timeoutIncrementalSearch);
		dbc_timeoutIncrementalSearch = null;
	}
}

function DBC_GetTopicHorizontalViewCode(idTopic)
{
	// returns 0 if a topic with a given ID (e.g. "12") is scrolled into view
	// horizontally, i.e. no horizontal scroll reqired to see the topic label;
	// returns -1 if the topic is to the left of the client area, 1 if to the
	// right
	var spanTopic = DBC_GetTopicSpan(idTopic);
	var xClientLeft = document.documentElement.scrollLeft;
	var xClientRight = xClientLeft + document.documentElement.clientWidth;
	var xSpanLeft = spanTopic.offsetLeft;
	var xSpanRight = spanTopic.offsetLeft + spanTopic.offsetWidth;
	if (xSpanLeft < xClientLeft)
		return -1;
	else
	if (xSpanRight > xClientRight)
		return 1;
	else
		return 0;
}

function DBC_GetTopicVerticalViewCode(idTopic)
{
	// returns 0 if a topic with a given ID (e.g. "12") is scrolled into
	// view vertically, i.e. no vertical scroll reqired to see the topic label;
	// returns -1 if the topic is above the client area, 1 if below
	var spanTopic = DBC_GetTopicSpan(idTopic);
	var yClientTop = document.documentElement.scrollTop;
	var yClientBottom = yClientTop + document.documentElement.clientHeight;
	var ySpanTop = spanTopic.offsetTop;
	var ySpanBottom = spanTopic.offsetTop + spanTopic.offsetHeight;
	if (ySpanTop < yClientTop)
		return -1;
	else
	if (ySpanBottom > yClientBottom)
		return 1;
	else
		return 0;
}

function DBC_IsTopicInView(idTopic)
{
	// returns true if a topic with a given ID is in view, i.e. no horizontal
	// or vertical scrolling is required to see any part of its label
	return (DBC_GetTopicHorizontalViewCode(idTopic) == 0) &&
		   (DBC_GetTopicVerticalViewCode(idTopic) == 0);
}

function DBC_ScrollSelectedTopicIntoView()
{
	// ensures that the label of the selected topic is in view, i.e. no
	// scrolling required to see at least its top-left corner
	if (dbc_idTopicSelected != null)
		DBC_ScrollTopicIntoView(dbc_idTopicSelected);
}

function DBC_ScrollTopicIntoView(idTopic)
{
	// ensures that the label of the topic with a given ID (e.g. "12") is in
	// view, i.e. no scrolling required to see at least its top-left corner...

	// get measurements
	var spanTopic = DBC_GetTopicSpan(idTopic);
	var xClientLeft = document.documentElement.scrollLeft;
	var xClientRight = xClientLeft + document.documentElement.clientWidth;
	var yClientTop = document.documentElement.scrollTop;
	var yClientBottom = yClientTop + document.documentElement.clientHeight;
	var xLabelLeft = spanTopic.offsetLeft - 9/*icon-width*/ - 4/*icon-margin*/
		- 2/*margin*/;
	var xLabelRight = spanTopic.offsetLeft + spanTopic.offsetWidth
		+ 1/*margin*/;
	var yLabelTop = spanTopic.offsetTop - 1/*margin*/;
	var yLabelBottom = spanTopic.offsetTop + spanTopic.offsetHeight
		+ 4/*margin*/;

	// scroll horizontally as needed
	if (xLabelRight > xClientRight)
		xClientLeft += xLabelRight - xClientRight;
	if (xLabelLeft < xClientLeft)
		xClientLeft = xLabelLeft;
	if (document.documentElement.scrollLeft != xClientLeft)
		document.documentElement.scrollLeft = xClientLeft;

	// scroll vertically as needed
	if (yLabelBottom > yClientBottom)
		yClientTop += yLabelBottom - yClientBottom;
	if (yLabelTop < yClientTop)
		yClientTop = yLabelTop;
	if (document.documentElement.scrollTop != yClientTop)
		document.documentElement.scrollTop = yClientTop;
}

function DBC_SelectFirstTopic(fLoad)
{
	// selects the topmost topic, if any; loads it if <fLoad> is true
	var idTopic = DBC_GetFirstTopic();
	if (idTopic != null)
		DBC_SelectTopic(idTopic, fLoad);
}

function DBC_SelectLastVisibleTopic(fLoad)
{
	// selects the bottommost visible topic, if any; loads it if <fLoad> is true
	var idTopic = DBC_GetLastVisibleTopic();
	if (idTopic != null)
		DBC_SelectTopic(idTopic, fLoad);
}

function DBC_SelectNextVisibleTopic()
{
	// selects the next topic (vertically) after the selected topic, if any,
	// not including collapsed topics
	if (dbc_idTopicSelected == null)
		return; // no topic selected
	var idTopic = DBC_GetNextVisibleTopic(dbc_idTopicSelected);
	if (idTopic != null)
		DBC_SelectTopic(idTopic, false);
}

function DBC_SelectPreviousVisibleTopic()
{
	// selects the previous topic (vertically) before the selected topic, if
	// any, not including collapsed topics
	if (dbc_idTopicSelected == null)
		return; // no topic selected
	var idTopic = DBC_GetPreviousVisibleTopic(dbc_idTopicSelected);
	if (idTopic != null)
		DBC_SelectTopic(idTopic, false);
}

function DBC_SelectLoadedTopic()
{
	// selects the topic that's loaded in the right pane, if any
	if (dbc_idTopicLoaded != null)
		DBC_SelectTopic(dbc_idTopicLoaded, false);
}

function DBC_Descend()
{
	// if the selected topic is a leaf, do nothing; if it's not expanded,
	// expand it; if it's already expanded, select to its first child
	if (dbc_idTopicSelected == null)
		return; // no topic selected
	var divTopic = DBC_GetTopicDiv(dbc_idTopicSelected);
	var divSubtree = DBC_GetSubtreeDiv(dbc_idTopicSelected);
	if (divSubtree == null)
		return; // selected topic is a leaf
	if (divSubtree.style.display.length == 0)
	{
		// subtree is collapsed -- expand it
		divSubtree.style.display = "block";
		DBC_GetTopicImgFromDiv(divTopic).src = "_Opened.gif";
	}
	else
	{
		// subtree is expanded -- descend to first child
		if (divSubtree.firstChild != null)
			DBC_SelectTopic(DBC_GetTopicId(divSubtree.firstChild), false);
	}
}

function DBC_Ascend()
{
	// if the selected topic is an expanded subtree, collapse it; otherwise
	// select its parent (if any)
	if (dbc_idTopicSelected == null)
		return; // no topic selected
	var divTopic = DBC_GetTopicDiv(dbc_idTopicSelected);
	var divSubtree = DBC_GetSubtreeDiv(dbc_idTopicSelected);
	if ((divSubtree != null) && (divSubtree.style.display.length > 0))
	{
		// subtree is expanded -- collapse it
		divSubtree.style.display = "";
		DBC_GetTopicImgFromDiv(divTopic).src = "_Closed.gif";
	}
	else
	{
		// ascend to parent (if any)
		divSubtree = divTopic.parentNode;
		if (divSubtree != null)
		{
			var idTopic = DBC_GetSubtreeId(divSubtree);
			if (idTopic != null)
				DBC_SelectTopic(idTopic, false);
		}
	}
}

function DBC_PageDown()
{
	// select the topic that's at most one screen below this one
	if (dbc_idTopicSelected == null)
		return; // no topic selected
	var idTopic = dbc_idTopicSelected;
	var divTopic = DBC_GetTopicDiv(idTopic);
	var yEnd = divTopic.offsetTop + document.documentElement.clientHeight;
	while (true)
	{
		var idTopicNext = DBC_GetNextVisibleTopic(idTopic);
		if (idTopicNext == null)
			break; // end of topics
		var divTopic = DBC_GetTopicDiv(idTopicNext);
		if (divTopic.offsetTop + divTopic.offsetHeight > yEnd)
			break; // one screen (vertical client area)
		idTopic = idTopicNext;
	}
	DBC_SelectTopic(idTopic, false);
}

function DBC_PageUp()
{
	// select the topic that's at most one screen above this one
	if (dbc_idTopicSelected == null)
		return; // no topic selected
	var idTopic = dbc_idTopicSelected;
	var divTopic = DBC_GetTopicDiv(idTopic);
	var yEnd = divTopic.offsetTop - document.documentElement.clientHeight;
	while (true)
	{
		var idTopicPrevious = DBC_GetPreviousVisibleTopic(idTopic);
		if (idTopicPrevious == null)
			break; // beginning of topics
		var divTopic = DBC_GetTopicDiv(idTopicPrevious);
		if (divTopic.offsetTop - divTopic.offsetHeight < yEnd)
			break; // one screen (vertical client area)
		idTopic = idTopicPrevious;
	}
	DBC_SelectTopic(idTopic, false);
}

function DBC_GetFirstTopic()
{
	// returns the topic ID of the topmost topic, or null if none
	if (document.body == undefined)
		return null;
	var el = document.body.firstChild;
	if (el == null)
		return null; // no topics
	while (true)
	{
		if (el.Topic != undefined)
			return DBC_GetTopicId(el);
		if ((el = el.nextSibling) == null)
			return null;
	}
}

function DBC_GetLastVisibleTopic()
{
	// returns the topic ID of the bottommost visible topic, or null if none...

	// set <el> to the last DIV
	if (document.body == undefined)
		return null;
	var el = document.body.lastChild;
	while (true)
	{
		if (el == null)
			return null; // empty TOC
		if (el.tagName == "DIV")
			break;
		el = el.previousSibling;
	}

	// find the rightmost visible descendent of <el>
	while (true)
	{
		if (DBC_GetSubtreeId(el) == null)
			return DBC_GetTopicId(el); // <el> is a leaf
		if (el.style.display.length == 0) // collapsed
			return DBC_GetTopicId(el.previousSibling);
		if ((el = el.lastChild) == null) // no children
			return DBC_GetTopicId(el.previousSibling);
	}
}

function DBC_GetNextVisibleTopic(idTopic)
{
	// returns topic ID of the next topic (vertically) after a given topic,
	// not including collapsed topics; null if none
	var divTopic = DBC_GetTopicDiv(idTopic);
	var el = divTopic.nextSibling;
	if (el != null)
	{
		if (DBC_GetSubtreeId(el) != null)
		{
			if ((el.style.display.length > 0) && (el.firstChild != null))
				return DBC_GetTopicId(el.firstChild); // first child
			else
				el = el.nextSibling;
		}
		if (el != null)
			return DBC_GetTopicId(el); // next sibling
	}
	while (true)
	{
		divSubtree = divTopic.parentNode;
		if (divSubtree == null)
			return null; // root
		idSubtree = DBC_GetSubtreeId(divSubtree);
		if (idSubtree == null)
			return null; // root
		divTopic = DBC_GetTopicDiv(idSubtree);
		var el = divTopic.nextSibling.nextSibling;
		if (el != null)
			return DBC_GetTopicId(el); // next sibling of parent
	}
}

function DBC_GetPreviousVisibleTopic(idTopic)
{
	// returns topic ID of the previous topic (vertically) before a given
	// topic, not including collapsed topics; null if none
	var divTopic = DBC_GetTopicDiv(idTopic);
	var el = divTopic.previousSibling;
	if (el != null)
	{
		// find rightmost visible descendant of <el>
		while (true)
		{
			if (DBC_GetSubtreeId(el) == null)
				return DBC_GetTopicId(el); // <el> is a leaf
			if (el.style.display.length == 0) // collapsed
				return DBC_GetTopicId(el.previousSibling);
			el = el.lastChild;
		}
	}
	else
	{
		// at left edge of subtree -- return parent
		var divSubtree = divTopic.parentNode;
		if (divSubtree != null)
			return DBC_GetSubtreeId(divSubtree); // parent
		return null; // beginning of list of list
	}
}

function DBC_GetTopicId(divTopic)
{
	// returns topic ID of <divTopic> (a DIV), e.g. "12", if the DIV has
	// id="Topic12"; null if none
	if (divTopic.id == undefined)
		return null;
	var a = divTopic.id.match(/^Topic(\d+)/);
	if (a == null)
		return null;
	else
		return a[1];
}

function DBC_GetSubtreeId(divSubtree)
{
	// returns topic ID of <divSubtree> (a DIV), e.g. "12", if the DIV has
	// id="Subtree12"; null if none
	if (divSubtree.id == undefined)
		return null;
	var a = divSubtree.id.match(/^Subtree(\d+)/);
	if (a == null)
		return null;
	else
		return a[1];
}

function DBC_GetTopicIdFromSpan(spanTopic)
{
	// returns the topic ID of <spanTopic> (a SPAN), e.g. "12", if the SPAN's
	// parent DIV has e.g. id="Topic12"; null if none
	if (spanTopic.parentElement == null)
		return null;
	return DBC_GetTopicId(spanTopic.parentElement);
}

function DBC_GetTopicDiv(idTopic)
{
	// returns a topic DIV given a topic ID, or null if none
	var divTopic = document.all["Topic" + idTopic];
	return (divTopic == undefined) ? null : divTopic;
}

function DBC_GetSubtreeDiv(idTopic)
{
	// returns a subtree DIV given a topic ID, or null if this is a leaf
	return document.all["Subtree" + idTopic];
}

function DBC_GetTopicImgFromDiv(divTopic)
{
	// gets the IMG element within a topic DIV
	return divTopic.all.tags("IMG")[0];
}

function DBC_GetTopicSpanFromDiv(divTopic)
{
	// gets the SPAN element within a topic DIV
	return divTopic.all.tags("SPAN")[0];
}

function DBC_GetTopicSpan(idTopic)
{
	// returns a topic SPAN given a topic ID, or null if none
	var divTopic = DBC_GetTopicDiv(idTopic);
	return (divTopic == null) ? null : DBC_GetTopicSpanFromDiv(divTopic);
}

/*---------- *_Index.htm Script ----------*/

// globals
dbi_idTopicLoaded = null; // topic ID (e.g. "12") of topic loaded in right pane
dbi_divTopicSelected = null; // DIV topic that user selected
dbi_adivTopic = null; // array of topics, populated by DBI_InitTopicArray()

function DBI_Find(strFind)
{
	// scroll list to specified lookup string...

	// initialize <dbi_adivTopic>; quit if it's empty
	DBI_InitTopicArray();
	if (dbi_adivTopic.length == 0)
		return; // empty TOC

	// set <idivTopic> to the index of the topic with the highest label
	// that's less than or equal to <strFind>; if all are greater than
	// <strFind>, select the first and quit
	var idivTopic = DBI_BinarySearch(strFind);
	if ((idivTopic == null) || (strFind.length == 0))
	{
		DBI_SelectFirstTopic(false); // TODO: scroll to top
		return;
	}

	// in case there are multiple topics with the same label, walk backwards
	// to find the first
	while (idivTopic > 0)
	{
		if (dbi_adivTopic[idivTopic - 1] != strFind)
			break;
		idivTopic--;
	}

	// if topic <idivTopic> doesn't have <strFind> as its initial prefix but
	// the next topic does, walk forward to that topic
	if ((idivTopic < dbi_adivTopic.length - 1) &&
		!StartsWith(dbi_adivTopic[idivTopic].SortKey, strFind) &&
		StartsWith(dbi_adivTopic[idivTopic + 1].SortKey, strFind))
		idivTopic++;
		
	// select topic <idivTopic>
	var divTopic = dbi_adivTopic[idivTopic];
	DBI_SelectTopic(divTopic, false);
	var spanTopic = DBI_GetTopicSpanFromDiv(divTopic);
	document.documentElement.scrollTop = spanTopic.offsetTop - 1/*margin*/;
}

function DBI_LoadSelectedTopic()
{
	// loads into the right pane whatever topic is currently selected
	if (dbi_divTopicSelected != null)
	{
		parent.DocTopic.location.href = dbi_divTopicSelected.Topic;
		parent.DocTopic.window.focus();
	}
}

function StartsWith(str, strPrefix)
{
	// returns true if <str> begins with <strPrefix>
	return str.substr(0, strPrefix.length) == strPrefix;
}

function DBI_BinarySearch(strFind)
{
	// binary-search TOC labels; return the <dbi_adivTopic> index of the
	// highest DIV with a label less than or equal to <strFind>; return null
	// if all are greater than <strFind>
	if (dbi_adivTopic[0].SortKey > strFind)
		return null; // all are greater than <strFind>
	var idivTopicMin = 0;
	var idivTopicMax = dbi_adivTopic.length;
	// invariant: min = in range, max = out of range
	while (idivTopicMin < idivTopicMax - 1)
	{
		var idivTopicMid = Math.floor((idivTopicMin + idivTopicMax) / 2);
		if (dbi_adivTopic[idivTopicMid].SortKey <= strFind)
			idivTopicMin = idivTopicMid;
		else
			idivTopicMax = idivTopicMid;
	}

	return idivTopicMin;
}

function DBI_InitTopicArray()
{
	// initialize <dbi_adivTopic> with an array of topics; set the SortKey
	// property of each DIV to the lowercase sort key
	if (dbi_adivTopic != null)
		return; // already initialized
	var adivAll = document.all.tags("DIV");
	dbi_adivTopic = new Array;
	for (var idivAll = 0; idivAll < adivAll.length; idivAll++)
	{
		var div = adivAll[idivAll];
		if (div.Topic == undefined)
			continue; // not a topic DIV
		div.SortKey = DBI_GetTopicSpanFromDiv(div).innerText.toLowerCase();
		dbi_adivTopic.push(div);
	}

	// clear "Please Wait..."
	if (parent.TabsFrame.window.DBT_SetFindTextBox != undefined)
		parent.TabsFrame.window.DBT_SetFindTextBox("");
}

function DBI_OnLoad()
{
	parent.dbi_OnTopicLoaded = DBI_OnTopicLoaded;
	DBI_SelectFirstTopic(false);
}

function DBI_OnTopicLoaded(idTopic, idOverloadListTopic)
{
	// a topic (e.g. "12") was loaded in the right pane -- update index UI;
	// if the topic has an associated overload list topic, use that one
	if (idOverloadListTopic != null)
		idTopic = idOverloadListTopic;
	if (dbi_idTopicLoaded != null)
	{
		var adivTopic = DBI_GetTopicDivs(dbi_idTopicLoaded);
		if (adivTopic == null)
			return; // no topics
		for (var idivTopic = 0; idivTopic < adivTopic.length; idivTopic++)
		{
			var divTopic = adivTopic[idivTopic];
			var spanTopic = DBI_GetTopicSpanFromDiv(divTopic);
			spanTopic.style.border = "solid 1px window";
		}
	}
	dbi_idTopicLoaded = idTopic;
	if (dbi_idTopicLoaded != null)
	{
		var adivTopic = DBI_GetTopicDivs(dbi_idTopicLoaded);
		if (adivTopic == null)
			return; // no topics
		for (var idivTopic = 0; idivTopic < adivTopic.length; idivTopic++)
		{
			var divTopic = adivTopic[idivTopic];
			var spanTopic = DBI_GetTopicSpanFromDiv(divTopic);
			spanTopic.style.border = "solid 1px threedshadow";
		}
	}
}

function DBI_SelectTopic(divTopic, fLoad)
{
	// select a topic given its DIV; load it if <fLoad> is true
	if (dbi_divTopicSelected != null)
	{
		// deselect old topic index entry
		var s = DBI_GetTopicSpanFromDiv(dbi_divTopicSelected).style;
		s.color = "windowtext";
		s.backgroundColor = "window";
	}
	dbi_divTopicSelected = divTopic;
	if (dbi_divTopicSelected != null)
	{
		// select new topic index entries
		var spanTopic = DBI_GetTopicSpanFromDiv(dbi_divTopicSelected);
		var s = spanTopic.style;
		s.color = "captiontext";
		s.backgroundColor = "activecaption";

		// load topic page (if specified)
		if (fLoad)
		{
			parent.DocTopic.location.href = divTopic.Topic;
			if (parent.TabsFrame.window.DBT_SetFindTextBox != undefined)
				parent.TabsFrame.window.DBT_SetFindTextBox(spanTopic.innerText);
		}

		// make topic visible
		DBI_ScrollSelectedTopicIntoView();
	}
}

function DBI_OnMouseDown()
{
	// set <elSrc> to the clicked-on element
	var elSrc = event.srcElement;

	// set <divTopic> to the nearest enclosing DIV with a Topic attribute
	var divTopic = elSrc;
	while (true)
	{
		if (divTopic == null)
			return; // user didn't click on an index entry
		if (divTopic.Topic != undefined)
			break;
		divTopic = divTopic.parentElement;
	}

	// select <divTopic>
	DBI_SelectTopic(divTopic, true);
}

function DBI_OnMouseOver()
{
	// if the mouse is hovering over a topic label that's partially scrolled
	// off the screen, display its label as a tooltip
	var elSrc = event.srcElement;
	if (elSrc.tagName == "SPAN")
	{
		var divTopic = DBI_GetTopicDivFromSpan(elSrc);
		if (divTopic == null)
			return; // not a topic SPAN
		if (DBI_IsTopicInView(divTopic))
			elSrc.title = ""; // full topic label already in view -- no tooltip
		else
			elSrc.title = elSrc.innerText; // label partially hidden
	}
}

function DBI_OnKeyDown()
{
	switch (event.keyCode)
	{

	case 38: // up-arrow

		DBI_SelectPreviousTopic()
		return false;

	case 40: // down-arrow

		DBI_SelectNextTopic()
		return false;

	case 37: // left-arrow

		DBI_Ascend();
		return false;

	case 39: // right-arrow

		DBI_Descend();
		return false;

	case 33: // Page Up

		DBI_PageUp();
		return false;

	case 34: // Page Down

		DBI_PageDown();
		return false;

	case 36: // Home

		DBI_SelectFirstTopic(false);
		return false;

	case 35: // End

		DBI_SelectLastVisibleTopic(false);
		return false;

	case 13: // Enter

		DBI_SelectTopic(dbi_divTopicSelected, true);
		return false;

	}
}

function DBI_GetTopicHorizontalViewCode(divTopic)
{
	// returns 0 if a topic with a given DIV is scrolled into view horizontally,
	// i.e. no horizontal scroll reqired to see the topic label; returns -1 if
	// the topic is to the left of the client area, 1 if to the right
	var spanTopic = DBI_GetTopicSpanFromDiv(divTopic);
	var xClientLeft = document.documentElement.scrollLeft;
	var xClientRight = xClientLeft + document.documentElement.clientWidth;
	var xSpanLeft = spanTopic.offsetLeft;
	var xSpanRight = spanTopic.offsetLeft + spanTopic.offsetWidth;
	if (xSpanLeft < xClientLeft)
		return -1;
	else
	if (xSpanRight > xClientRight)
		return 1;
	else
		return 0;
}

function DBI_GetTopicVerticalViewCode(divTopic)
{
	// returns 0 if a topic with a given DIV is scrolled into view vertically,
	// i.e. no vertical scroll reqired to see the topic label; returns -1 if
	// the topic is above the client area, 1 if below
	var spanTopic = DBI_GetTopicSpanFromDiv(divTopic);
	var yClientTop = document.documentElement.scrollTop;
	var yClientBottom = yClientTop + document.documentElement.clientHeight;
	var ySpanTop = spanTopic.offsetTop;
	var ySpanBottom = spanTopic.offsetTop + spanTopic.offsetHeight;
	if (ySpanTop < yClientTop)
		return -1;
	else
	if (ySpanBottom > yClientBottom)
		return 1;
	else
		return 0;
}

function DBI_IsTopicInView(divTopic)
{
	// returns true if a topic with a given DIV is in view, i.e. no horizontal
	// or vertical scrolling is required to see any part of its label
	return (DBI_GetTopicHorizontalViewCode(divTopic) == 0) &&
		   (DBI_GetTopicVerticalViewCode(divTopic) == 0);
}

function DBI_ScrollSelectedTopicIntoView()
{
	// ensures that the label of the selected topic is in view, i.e. no
	// scrolling required to see at least its top-left corner
	if (dbi_divTopicSelected != null)
		DBI_ScrollTopicIntoView(dbi_divTopicSelected);
}

function DBI_ScrollTopicIntoView(divTopic)
{
	// ensures that the label of the topic with a given DIV is in view, i.e. no
	// scrolling required to see at least its top-left corner...

	// get measurements
	var spanTopic = DBI_GetTopicSpanFromDiv(divTopic);
	var yClientTop = document.documentElement.scrollTop;
	var yClientBottom = yClientTop + document.documentElement.clientHeight;
	var yLabelTop = spanTopic.offsetTop - 1/*margin*/;
	var yLabelBottom = spanTopic.offsetTop + spanTopic.offsetHeight
		+ 1/*margin*/;

	// scroll vertically as needed
	if (yLabelBottom > yClientBottom)
		yClientTop += yLabelBottom - yClientBottom;
	if (yLabelTop < yClientTop)
		yClientTop = yLabelTop;
	if (document.documentElement.scrollTop != yClientTop)
		document.documentElement.scrollTop = yClientTop;
}

function DBI_SelectFirstTopic(fLoad)
{
	// selects the topmost topic, if any; loads it if <fLoad> is true
	var divTopic = DBI_GetFirstTopic();
	if (divTopic != null)
		DBI_SelectTopic(divTopic, fLoad);
}

function DBI_SelectLastVisibleTopic(fLoad)
{
	// selects the bottommost visible topic, if any; loads it if <fLoad> is true
	var divTopic = DBI_GetLastVisibleTopic();
	if (divTopic != null)
		DBI_SelectTopic(divTopic, fLoad);
}

function DBI_SelectNextTopic()
{
	// selects the next topic (vertically) after the selected topic, if any
	if (dbi_divTopicSelected == null)
		return; // no topic selected
	var divTopic = DBI_GetNextTopic(dbi_divTopicSelected);
	if (divTopic != null)
		DBI_SelectTopic(divTopic, false);
}

function DBI_SelectPreviousTopic()
{
	// selects the previous topic (vertically) before the selected topic, if any
	if (dbi_divTopicSelected == null)
		return; // no topic selected
	var divTopic = DBI_GetPreviousTopic(dbi_divTopicSelected);
	if (divTopic != null)
		DBI_SelectTopic(divTopic, false);
}

function DBI_PageDown()
{
	// select the topic that's at most one screen below this one
	if (dbi_divTopicSelected == null)
		return; // no topic selected
	var divTopic = dbi_divTopicSelected;
	var yEnd = divTopic.offsetTop + document.documentElement.clientHeight;
	while (true)
	{
		var divTopicNext = DBI_GetNextTopic(divTopic);
		if (divTopicNext == null)
			break; // end of topics
		if (divTopic.offsetTop + divTopic.offsetHeight > yEnd)
			break; // one screen (vertical client area)
		divTopic = divTopicNext;
	}
	DBI_SelectTopic(divTopic, false);
}

function DBI_PageUp()
{
	// select the topic that's at most one screen above this one
	if (dbi_divTopicSelected == null)
		return; // no topic selected
	var divTopic = dbi_divTopicSelected;
	var yEnd = divTopic.offsetTop - document.documentElement.clientHeight;
	while (true)
	{
		var divTopicPrevious = DBI_GetPreviousTopic(divTopic);
		if (divTopicPrevious == null)
			break; // beginning of topics
		if (divTopic.offsetTop - divTopic.offsetHeight < yEnd)
			break; // one screen (vertical client area)
		divTopic = divTopicPrevious;
	}
	DBI_SelectTopic(divTopic, false);
}

function DBI_GetFirstTopic()
{
	// returns the DIV of the topmost topic, or null if none
	if (document.body == undefined)
		return null;
	var el = document.body.firstChild;
	while (true)
	{
		if (el == null)
			return null;
		if (el.Topic != undefined)
			return el;
		el = el.nextSibling;
	}
}

function DBI_GetLastVisibleTopic()
{
	// returns the DIV of the bottommost visible topic, or null if none...
	if (document.body == undefined)
		return null;
	var el = document.body.lastChild;
	while (true)
	{
		if (el == null)
			return null;
		if (el.Topic != undefined)
			return el;
		el = el.previousSibling;
	}
}

function DBI_GetNextTopic(divTopic)
{
	// returns the DIV of the next topic (vertically) after a given topic
	divTopic = divTopic.nextSibling;
	if ((divTopic != null) && (divTopic.tagName == "DIV"))
		return divTopic;
	else
		return null;
}

function DBI_GetPreviousTopic(divTopic)
{
	// returns the DIV of the previous topic (vertically) before a given topic
	divTopic = divTopic.previousSibling;
	if ((divTopic != null) && (divTopic.tagName == "DIV"))
		return divTopic;
	else
		return null;
}

function DBI_GetTopicId(divTopic)
{
	// returns topic ID of <divTopic> (a DIV), e.g. "12", if the DIV has
	// id="Topic12"; null if none
	if (divTopic.id == undefined)
		return null;
	var a = divTopic.id.match(/^Topic(\d+)/);
	if (a == null)
		return null;
	else
		return a[1];
}

function DBI_GetTopicDivFromSpan(spanTopic)
{
	// returns the DIV enclosing <spanTopic> (a SPAN), if the SPAN's parent DIV
	// has e.g. id="Topic12"; null if none
	if (spanTopic.parentElement == null)
		return null;
	return spanTopic.parentElement;
}

function DBI_GetTopicDivs(idTopic)
{
	// returns a collection of topic DIVs given a topic ID (e.g. "12"), or
	// null if there are no topic DIVs
	var a = document.all["Topic" + idTopic];
	if (a == undefined)
		return null
	else
	if (a.length == undefined)
	{
		var a2 = new Array(1);
		a2[0] = a;
		return a2;
	}
	else
		return a;
}

function DBI_GetTopicSpanFromDiv(divTopic)
{
	// gets the SPAN element within a topic DIV
	return divTopic.all.tags("SPAN")[0];
}

function DBI_OnFocus()
{
	//DBI_ScrollSelectedTopicIntoView();
}

