var undefined;

function Cookie(document, name, hours, path, domain, secure) {
    // all predefined property variables of this object begin with '$' to distinguish
    // them from other properties, which are the ones to be stored in the cookie 
    this.$document = document;
    this.$name     = name;
    if (hours) {
        this.$expiration = new Date((new Date()).getTime() + hours * 3600000);
    } else {
        this.$expiration = null;
    }
    this.$path   = path   ? path   : null;
    this.$domain = domain ? domain : null;
    this.$secure = secure ? true   : false;
}

Cookie.prototype.store = function() {
    // consolidate all the properties of the cookie. our separators are ':' between 
    // property name and value, and '&' between properties.
    var cookieVal = "";
    for (var prop in this) {
        // ignore methods and properties that start with '$'
        if ((prop.charAt(0) == '$') || ((typeof this[prop]) == 'function'))
            continue;
        // append the '&' separator to the previous value
        if (cookieVal != "")
            cookieVal += '&';
        cookieVal += prop + ':' + escape(this[prop]);
    }
    
    // put together the complete cookie string, which includes the cookie attributes
    var cookie = this.$name + '=' + cookieVal;
    if (this.$expiration) cookie += '; expires=' + this.$expiration.toGMTString();
    if (this.$path)       cookie += '; path='    + this.$path;
    if (this.$domain)     cookie += '; domain='  + this.$domain;
    if (this.$secure)     cookie += '; secure';
        
    this.$document.cookie = cookie;     // store the cookie
}

Cookie.prototype.load = function() {
    // get the list of cookies that pertain to this document
    var allCookies = this.$document.cookie;
    if (allCookies == "") return false;
    
    // extract just our named cookie
    var start = allCookies.indexOf(this.$name + '=');
    if (start == -1) return false;      // no cookie defined for this page
    start += this.$name.length + 1;     // skip name and equals sign
    var end = allCookies.indexOf(';', start);
    if (end == -1) end = allCookies.length;
    var cookieVal = allCookies.substring(start, end);
    
    // break the value down into individual properties and values. '&' separates the
    // property/value pairs, and ':' separates properties from values. save the 
    // results as the object's properties
    var a = cookieVal.split('&');
    for (var i = 0; i < a.length; i++) {
        a[i] = a[i].split(':');
        this [a[i][0]] = unescape(a[i][1]);   
    }

    return true;
}

//*************************************************************************************************
//*
//*	JS TreeMenu code (c)2001 Scriptorium - BD
//*
//*	http://scriptor.cjb.net
//*
//*************************************************************************************************

var menu_nodes = new Array();
var menu_state = new Array();
// values:
//  2         => only open because it's the current topic
//  1         => explicitly open
//  0         => explicitly closed
//  undefined => default closed
	
var treemenu_cookie;        // initialized in the IndexTree constructor

function TreeNode(expand, caption, link, target, number, level, type) {
	// Node properties
	this.expand  = expand;
	this.caption = caption;
	this.link    = link;
	this.target  = target;
	this.number  = number;
	this.level   = level;
        this.type    = type;
	this.next    = null;
	this.subtree = null;
        this.mother  = null;
}

TreeNode.prototype.AddSibling = function(expand,caption,link,target, number, level, type) {
	s = this;
	while (s.next != null) {
		s = s.next;	
	}
	s.next = new TreeNode(expand,caption,link,target, number, level, type);
        s.next.mother = this.mother;
	return s.next;
}

TreeNode.prototype.ImageURL = function() {
    if (this.type == 'folder') {
        return (this.expand ? image_openfolder : image_folder);
    } else if (this.type == 'file') {
        return image_file;
    } else {
        return image_bullet;
    }
}

TreeNode.prototype.Display = function() {
	for (var i=0; i<this.level; i++) {
                document.write('<img src="' + image_blank + '" valign=middle>');
        }
 	if (this.subtree == null) {
		document.write('<img src="' + image_blank + '" valign=middle><img src="' + this.ImageURL() + '" valign=middle>');
	} else {
	    	document.write('<a href="javascript: ' + (this.expand ? 'treeCollapse(' : 'treeExpand(') + this.number + ')"><img src="' + (this.expand ? image_minus : image_plus) + '" valign=middle border=no><img src="' + this.ImageURL() + '" valign=middle border=no></a>');
        }
        //if (this.current) this.caption = '<b>&gt;&gt ' + this.caption + ' &lt;&lt</b>';
        if (this.current) this.caption = '<b>' + this.caption + '</b>';        
	if (this.link == '') {
		document.writeln(' ' + this.caption + '<br>');
	} else {
		document.writeln(' <a href="' + this.link + '" target="' + this.target + '">' + this.caption + '</a><br>');
	}
	if (this.subtree!=null && this.expand) this.subtree.Display();
	if (this.next != null) this.next.Display();
}

TreeNode.prototype.ExpandURL = function(url) {

    // find the node that has the current url
    function findURL(tree, url) {
        for (var n = tree; n != null; n = n.next) {
            if (n.link == url) {
                return n;
            } else if (n.subtree != null) {
                var found = findURL(n.subtree, url);
                if (found) return found;
            }
        }
        return null;
    }
    var node = findURL(this, url);
    if (node == null) {
        alert("javascript: internal error: couldn't find current url " + url);
        return;
    }
        
    // mark it as the current node
    node.current = true;
    
    // expand the node and all of its parents
    if (url == treemenu_cookie.previous_url) {
        while (node != null) {
            // respect any explicitly opened/closed nodes
            if (menu_state[node.number] == undefined) {
                node.expand = 1;
                menu_state[node.number] = 2;    // only open because it's the current document
            }
            node = node.mother;
        }
    } else {
        // new page. clear out all the ones that are only open because it was the current page
        for (var i = 0; i < menu_state.length; i++) {
            if (menu_state[i] == 2) {
                menu_state[i] = undefined;
                menu_nodes[i].expand = 0;
            }
        }
        // now expand our node and all the parents
        while (node != null) {
            node.expand = 1;
            if (menu_state[node.number] != 1) { 
                menu_state[node.number] = 2;    // 2 means: only open because it's the current document
            }
            node = node.mother;
        }    
    }
    
    // save what we did
    treemenu_cookie.treemenu_state = menu_state.join('|');
    treemenu_cookie.previous_url = url;
    treemenu_cookie.store();
}

var buildProgress = 0;

function addToTree(arr, mother) {
	var start = buildProgress;
	var m = new TreeNode(menu_state[start] > 0,arr[start][1],arr[start][2],arr[start][3], start, arr[start][0], arr[start][4]);
        menu_nodes[start] = m;
	buildProgress++;
        if (mother != null) m.mother = mother;

	var done = (buildProgress >= arr.length);
	var s = m;

	while (!done) {
		if (arr[start][0] == arr[buildProgress][0]) {
			j = buildProgress;
			s = s.AddSibling(menu_state[j] > 0,arr[j][1],arr[j][2],arr[j][3], j, arr[j][0], arr[j][4]);
                        menu_nodes[j] = s;
			buildProgress++;
		} else if (arr[start][0] < arr[buildProgress][0]) {
			s.subtree = addToTree(arr, s);
		} else if (arr[start][0] > arr[buildProgress][0]) {
			done = true;
		}
		done = done || (buildProgress >= arr.length);
	}
	return m;
}

function treeExpand(n) {
	menu_state[n] = 1;
	treemenu_cookie.treemenu_state = menu_state.join('|');
        treemenu_cookie.store();
	location.reload();
}

function treeCollapse(n) {
	menu_state[n] = 0;
	treemenu_cookie.treemenu_state = menu_state.join('|');
        treemenu_cookie.store();
	location.reload();
}

function buildMenu(arr) {
	if (treemenu_cookie.treemenu_state != undefined) {
		menu_state = treemenu_cookie.treemenu_state.split('|');
        }
	buildProgress = 0;
	return addToTree(arr, null);
}

/*----------------------------------------------------------------------------\
|                       Cross Browser Tree Widget 1.12                        |
|-----------------------------------------------------------------------------|
|                          Created by Emil A Eklund                           |
|                  (http://webfx.eae.net/contact.html#emil)                   |
|                      For WebFX (http://webfx.eae.net/)                      |
|-----------------------------------------------------------------------------|
| An object based tree widget,  emulating the one found in microsoft windows, |
| with persistence using cookies. Works in IE 5+, Mozilla and konqueror 3.    |
|-----------------------------------------------------------------------------|
|                   Copyright (c) 1999 - 2002 Emil A Eklund                   |
|-----------------------------------------------------------------------------|
| This software is provided "as is", without warranty of any kind, express or |
| implied, including  but not limited  to the warranties of  merchantability, |
| fitness for a particular purpose and noninfringement. In no event shall the |
| authors or  copyright  holders be  liable for any claim,  damages or  other |
| liability, whether  in an  action of  contract, tort  or otherwise, arising |
| from,  out of  or in  connection with  the software or  the  use  or  other |
| dealings in the software.                                                   |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| This  software is  available under the  three different licenses  mentioned |
| below.  To use this software you must chose, and qualify, for one of those. |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| The WebFX Non-Commercial License          http://webfx.eae.net/license.html |
| Permits  anyone the right to use the  software in a  non-commercial context |
| free of charge.                                                             |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| The WebFX Commercial license           http://webfx.eae.net/commercial.html |
| Permits the  license holder the right to use  the software in a  commercial |
| context. Such license must be specifically obtained, however it's valid for |
| any number of  implementations of the licensed software.                    |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| GPL - The GNU General Public License    http://www.gnu.org/licenses/gpl.txt |
| Permits anyone the right to use and modify the software without limitations |
| as long as proper  credits are given  and the original  and modified source |
| code are included. Requires  that the final product, software derivate from |
| the original  source or any  software  utilizing a GPL  component, such  as |
| this, is also licensed under the GPL license.                               |
|-----------------------------------------------------------------------------|
| 2001-01-10 | Original Version Posted.                                       |
| 2001-03-18 | Added getSelected and get/setBehavior  that can make it behave |
|            | more like windows explorer, check usage for more information.  |
| 2001-09-23 | Version 1.1 - New features included  keyboard  navigation (ie) |
|            | and the ability  to add and  remove nodes dynamically and some |
|            | other small tweaks and fixes.                                  |
| 2002-01-27 | Version 1.11 - Bug fixes and improved mozilla support.         |
| 2002-06-11 | Version 1.12 - Fixed a bug that prevented the indentation line |
|            | from  updating correctly  under some  circumstances.  This bug |
|            | happened when removing the last item in a subtree and items in |
|            | siblings to the remove subtree where not correctly updated.    |
| 2002-06-13 | Fixed a few minor bugs cased by the 1.12 bug-fix.              |
|-----------------------------------------------------------------------------|
| Created 2000-12-11 | All changes are in the log above. | Updated 2002-06-11 |
\----------------------------------------------------------------------------*/

var treeHandler = {
	idCounter : 0,
	idPrefix  : "tree-object-",
	all       : {},
	selected  : null,
	onSelect  : null, /* should be part of tree, not handler */
	getId     : function() { return this.idPrefix + this.idCounter++; },
	toggle    : function (oItem) { this.all[oItem.id.replace('-plus','')].toggle(); },
	select    : function (oItem) { this.all[oItem.id.replace('-icon','')].select(); },
	focus     : function (oItem) { this.all[oItem.id.replace('-anchor','')].focus(); },
	blur      : function (oItem) { this.all[oItem.id.replace('-anchor','')].blur(); },
	keydown   : function (oItem, e) { return this.all[oItem.id].keydown(e.keyCode); },
	cookie    : null,       // initialized in the IndexTree constructor
	insertHTMLBeforeEnd	:	function (oElement, sHTML) {
		if (oElement.insertAdjacentHTML != null) {
			oElement.insertAdjacentHTML("BeforeEnd", sHTML)
			return;
		}
		var df;	// DocumentFragment
		var r = oElement.ownerDocument.createRange();
		r.selectNodeContents(oElement);
		r.collapse(false);
		df = r.createContextualFragment(sHTML);
		oElement.appendChild(df);
	}
};

/*
 * TreeAbstractNode class
 */

function TreeAbstractNode(sText, sAction, sType) {
	this.childNodes  = [];
	this.id     = treeHandler.getId();
	this.text   = sText;
	this.action = sAction || null;
        this.type   = sType;
	this._last  = false;
	treeHandler.all[this.id] = this;
}

/*
 * To speed thing up if you're adding multiple nodes at once (after load)
 * use the bNoIdent parameter to prevent automatic re-indentation and call
 * the obj.ident() method manually once all nodes has been added.
 */

TreeAbstractNode.prototype.imageURL = function () {
    if (this.type == 'folder')
        return this.open ? image_openfolder : image_folder;
    else if (this.type == 'file')
        return image_file;
    else
        return image_bullet;
}

TreeAbstractNode.prototype.add = function (node, bNoIdent) {
	node.parentNode = this;
	this.childNodes[this.childNodes.length] = node;
	var root = this;
	if (this.childNodes.length >=2) {
		this.childNodes[this.childNodes.length -2]._last = false;
	}
	while (root.parentNode) { root = root.parentNode; }
	if (root.rendered) {
		if (this.childNodes.length >= 2) {
			document.getElementById(this.childNodes[this.childNodes.length -2].id + '-plus').src = ((this.childNodes[this.childNodes.length -2].branch)?((this.childNodes[this.childNodes.length -2].open)?image_tminus:image_tplus):image_t);
			if (this.childNodes[this.childNodes.length -2].branch) {
				this.childNodes[this.childNodes.length -2].plusIcon = image_tplus;
				this.childNodes[this.childNodes.length -2].minusIcon = image_tminus;
			}
			this.childNodes[this.childNodes.length -2]._last = false;
		}
		this._last = true;
		var foo = this;
		while (foo.parentNode) {
			for (var i = 0; i < foo.parentNode.childNodes.length; i++) {
				if (foo.id == foo.parentNode.childNodes[i].id) { break; }
			}
			if (++i == foo.parentNode.childNodes.length) { foo.parentNode._last = true; }
			else { foo.parentNode._last = false; }
			foo = foo.parentNode;
		}
		treeHandler.insertHTMLBeforeEnd(document.getElementById(this.id + '-cont'), node.toString());
		if (!this.branch) { this.branch = true; this.collapse(true); }
		if (!bNoIdent) { this.indent(); }
	}
	return node;
}

TreeAbstractNode.prototype.toggle = function() {
	if (this.branch) {
		if (this.open) { this.collapse(); }
		else { this.expand(); }
}	}

TreeAbstractNode.prototype.select = function() {
	document.getElementById(this.id + '-anchor').focus();
}

TreeAbstractNode.prototype.deSelect = function() {
        var anchor = document.getElementById(this.id + '-anchor');
	if (anchor)
            anchor.className = '';
	treeHandler.selected = null;
}

TreeAbstractNode.prototype.focus = function() {
	if ((treeHandler.selected) && (treeHandler.selected != this)) { treeHandler.selected.deSelect(); }
	treeHandler.selected = this;
        var anchor = document.getElementById(this.id + '-anchor');
	if (anchor) {
	    anchor.className = 'selected';
	    anchor.focus();
        }
	if (treeHandler.onSelect) { treeHandler.onSelect(this); }
}

TreeAbstractNode.prototype.blur = function() {
	document.getElementById(this.id + '-anchor').className = 'selected-inactive';
}

TreeAbstractNode.prototype.doExpand = function() {
	this.open = true;
	document.getElementById(this.id + '-icon').src = this.imageURL();
	if (this.childNodes.length) {  document.getElementById(this.id + '-cont').style.display = 'block'; }
        var prop = this.id.substr(treeHandler.idPrefix.length,this.id.length - treeHandler.idPrefix.length);
	treeHandler.cookie[prop] = 1;
        treeHandler.cookie.store();
}

TreeAbstractNode.prototype.doCollapse = function() {
	this.open = false;
	document.getElementById(this.id + '-icon').src = this.imageURL();
	if (this.childNodes.length) { document.getElementById(this.id + '-cont').style.display = 'none'; }
        var prop = this.id.substr(treeHandler.idPrefix.length,this.id.length - treeHandler.idPrefix.length);
	treeHandler.cookie[prop] = 0;
        treeHandler.cookie.store();
}

TreeAbstractNode.prototype.expandAll = function() {
	this.expandChildren();
	if ((this.branch) && (!this.open)) { this.expand(); }
}

TreeAbstractNode.prototype.expandChildren = function() {
	for (var i = 0; i < this.childNodes.length; i++) {
		this.childNodes[i].expandAll();
} }

TreeAbstractNode.prototype.collapseAll = function() {
	this.collapseChildren();
	if ((this.branch) && (this.open)) { this.collapse(true); }
}

TreeAbstractNode.prototype.collapseChildren = function() {
	for (var i = 0; i < this.childNodes.length; i++) {
		this.childNodes[i].collapseAll();
} }

TreeAbstractNode.prototype.indent = function(lvl, del, last, level, nodesLeft) {
	/*
	 * Since we only want to modify items one level below ourself,
	 * and since the rightmost indentation position is occupied by
	 * the plus icon we set this to -2
	 */
	if (lvl == null) { lvl = -2; }
	var state = 0;
	for (var i = this.childNodes.length - 1; i >= 0 ; i--) {
		state = this.childNodes[i].indent(lvl + 1, del, last, level);
		if (state) { return; }
	}
	if (del) {
		if ((level >= this._level) && (document.getElementById(this.id + '-plus'))) {
			if (this.branch) {
				document.getElementById(this.id + '-plus').src = (this.open)?image_lminus:image_lplus;
				this.plusIcon = image_lplus;
				this.minusIcon = image_lminus;
			}
			else if (nodesLeft) { document.getElementById(this.id + '-plus').src = image_l; }
			return 1;
	}	}
	var foo = document.getElementById(this.id + '-indent-' + lvl);
	if (foo) {
		if ((foo._last) || ((del) && (last))) { foo.src = image_blank; }
		else { foo.src = image_i; }
	}
	return 0;
}

TreeAbstractNode.prototype.expandURL = function(url) {
    // find the node that has the current url
    function findURL(tree, url) {
        if (tree.action == url) return tree;
        if (tree.childNodes.length == 0) return null;
        for (var i = 0; i < tree.childNodes.length; i++) {
            if (tree.childNodes[i].action == url) {
                return tree.childNodes[i];
            } else if (tree.childNodes[i].childNodes.length) {
                var found = findURL(tree.childNodes[i], url);
                if (found) return found;
            }
        }
        return null;
    }
    var node = findURL(this, url);
    if (node == null) {
        alert("javascript: internal error: couldn't find current url " + url);
        return;
    }

    // mark it as the current node
    node.current = true;
    
    // expand the node and all of its parents
    if (url == treeHandler.cookie.previous_url) {
        while (node != null) {
            // respect any explicitly opened/closed nodes
            var prop = node.id.substr(treeHandler.idPrefix.length,node.id.length - treeHandler.idPrefix.length);        
            if (treeHandler.cookie[prop] == undefined) {
                node.open = true;
                treeHandler.cookie[prop] = 2;    // 2 means: only open because it's the current document
            }
            node = node.parentNode;
        }
    } else {
        // new page. close all the ones that are only open because it was the current page
        for (var i in treeHandler.all) {
            var prop = i.substr(treeHandler.idPrefix.length, i.length - treeHandler.idPrefix.length);   
            if (treeHandler.cookie[prop] == 2) {
                delete treeHandler.cookie[prop];
                treeHandler.all[i].open = false;
            }
        }
        // now expand our node and all the parents
        while (node != null) {
            node.open = true;
            var prop = node.id.substr(treeHandler.idPrefix.length,node.id.length - treeHandler.idPrefix.length);        
            if (treeHandler.cookie[prop] != 1) { 
                treeHandler.cookie[prop] = 2;    // 2 means: only open because it's the current document
            }
            node = node.parentNode;
        }    
    }
    
    // store the current url with the other changes that we made
    treeHandler.cookie.previous_url = url;
    treeHandler.cookie.store();
}

/*
 * Tree class
 */

function Tree(sText, sAction, sType) {
	this.base = TreeAbstractNode;
	this.base(sText);
        this.treeAction = sAction;
        this.type = sType ? sType : 'folder';
	/* Defaults to open */
        var prop = this.id.substr(treeHandler.idPrefix.length,this.id.length - treeHandler.idPrefix.length);        
	this.open = (treeHandler.cookie[prop] == 0) ? false : true;
	this.branch    = true;
	this.rendered  = false;
	this.onSelect  = null;
}

Tree.prototype = new TreeAbstractNode;

Tree.prototype.getSelected = function() {
	if (treeHandler.selected) { return treeHandler.selected; }
	else { return null; }
}

Tree.prototype.expand = function() {
	this.doExpand();
}

Tree.prototype.collapse = function(b) {
	if (!b) { this.focus(); }
	this.doCollapse();
}

Tree.prototype.getFirst = function() {
	return null;
}

Tree.prototype.getLast = function() {
	return null;
}

Tree.prototype.getNextSibling = function() {
	return null;
}

Tree.prototype.getPreviousSibling = function() {
	return null;
}

Tree.prototype.keydown = function(key) {
	if (key == 39) {
		if (!this.open) { this.expand(); }
		else if (this.childNodes.length) { this.childNodes[0].select(); }
		return false;
	}
	if (key == 37) { this.collapse(); return false; }
	if ((key == 40) && (this.open) && (this.childNodes.length)) { this.childNodes[0].select(); return false; }
	return true;
}

Tree.prototype.toString = function() {
        //if (this.current) this.text = '<b>&gt;&gt ' + this.text + ' &lt;&lt</b>';
        if (this.current) this.text = '<b>' + this.text + '</b>';
	var str = "<div id=\"" + this.id + "\" ondblclick=\"treeHandler.toggle(this);\" class=\"tree-item\" onkeydown=\"return treeHandler.keydown(this, event)\">";
	str += "<img id=\"" + this.id + "-icon\" class=\"tree-icon\" src=\"" + this.imageURL() + "\" onclick=\"treeHandler.select(this);\">" + (this.treeAction?("<a href=\"" + this.treeAction + "\" id=\"" + this.id + "-anchor\" onfocus=\"treeHandler.focus(this);\" onblur=\"treeHandler.blur(this);\">"):"") + this.text + (this.treeAction?"</a>":"") + "</div>";
	str += "<div id=\"" + this.id + "-cont\" class=\"tree-container\" style=\"display: " + ((this.open)?'block':'none') + ";\">";
	for (var i = 0; i < this.childNodes.length; i++) {
		str += this.childNodes[i].toString(i, this.childNodes.length);
	}
	str += "</div>";
	this.rendered = true;
	return str;
};

/*
 * TreeItem class
 */

function TreeItem(sText, sAction, eParent, sType) {
	this.base = TreeAbstractNode;
	this.base(sText, sAction);
	/* Defaults to close */
        var prop = this.id.substr(treeHandler.idPrefix.length,this.id.length - treeHandler.idPrefix.length);        
	this.open   = (treeHandler.cookie[prop] > 0) ? true : false;
        this.type = sType;
	if (eParent) { eParent.add(this); }
}

TreeItem.prototype = new TreeAbstractNode;

TreeItem.prototype.expand = function() {
	this.doExpand();
	document.getElementById(this.id + '-plus').src = this.minusIcon;
}

TreeItem.prototype.collapse = function(b) {
	if (!b) { this.focus(); }
	this.doCollapse();
	document.getElementById(this.id + '-plus').src = this.plusIcon;
}

TreeItem.prototype.getFirst = function() {
	return this.childNodes[0];
}

TreeItem.prototype.getLast = function() {
	if (this.childNodes[this.childNodes.length - 1].open) { return this.childNodes[this.childNodes.length - 1].getLast(); }
	else { return this.childNodes[this.childNodes.length - 1]; }
}

TreeItem.prototype.getNextSibling = function() {
	for (var i = 0; i < this.parentNode.childNodes.length; i++) {
		if (this == this.parentNode.childNodes[i]) { break; }
	}
	if (++i == this.parentNode.childNodes.length) { return this.parentNode.getNextSibling(); }
	else { return this.parentNode.childNodes[i]; }
}

TreeItem.prototype.getPreviousSibling = function(b) {
	for (var i = 0; i < this.parentNode.childNodes.length; i++) {
		if (this == this.parentNode.childNodes[i]) { break; }
	}
	if (i == 0) { return this.parentNode; }
	else {
		if ((this.parentNode.childNodes[--i].open) || (b && this.parentNode.childNodes[i].branch)) { return this.parentNode.childNodes[i].getLast(); }
		else { return this.parentNode.childNodes[i]; }
} }

TreeItem.prototype.keydown = function(key) {
	if ((key == 39) && (this.branch)) {
		if (!this.open) { this.expand(); }
		else { this.getFirst().select(); }
		return false;
	}
	else if (key == 37) {
		if (this.open) { this.collapse(); }
		else { this.parentNode.select(); }
		return false;
	}
	else if (key == 40) {
		if (this.open) { this.getFirst().select(); }
		else {
			var sib = this.getNextSibling();
			if (sib) { sib.select(); }
		}
		return false;
	}
	else if (key == 38) { this.getPreviousSibling().select(); return false; }
	return true;
}

TreeItem.prototype.toString = function (nItem, nItemCount) {
	var foo = this.parentNode;
	var indent = '';
	if (nItem + 1 == nItemCount) { this.parentNode._last = true; }
	var i = 0;
	while (foo.parentNode) {
		foo = foo.parentNode;
		indent = "<img id=\"" + this.id + "-indent-" + i + "\" src=\"" + ((foo._last)?image_blank:image_i) + "\">" + indent;
		i++;
	}
	this._level = i;
	if (this.childNodes.length) { this.branch = 1; }
	else { this.open = false; }
	var label = this.text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
        // if (this.current) label = '<b>&gt;&gt ' + label + ' &lt;&lt</b>';
        if (this.current) label = '<b>' + label + '</b>';
	var str = "<div id=\"" + this.id + "\" ondblclick=\"treeHandler.toggle(this);\" class=\"tree-item\" onkeydown=\"return treeHandler.keydown(this, event)\">";
	str += indent;
	str += "<img id=\"" + this.id + "-plus\" src=\"" + ((this.branch)?((this.open)?((this.parentNode._last)?image_lminus:image_tminus):((this.parentNode._last)?image_lplus:image_tplus)):((this.parentNode._last)?image_l:image_t)) + "\" onclick=\"treeHandler.toggle(this);\">"
	str += "<img id=\"" + this.id + "-icon\" class=\"tree-icon\" src=\"" + this.imageURL() + "\" onclick=\"treeHandler.select(this);\">" + (this.action?("<a href=\"" + this.action + "\" id=\"" + this.id + "-anchor\" onfocus=\"treeHandler.focus(this);\" onblur=\"treeHandler.blur(this);\">"):"") + label + (this.action?"</a>":"") + "</div>"; 
	str += "<div id=\"" + this.id + "-cont\" class=\"tree-container\" style=\"display: " + ((this.open)?'block':'none') + ";\">";
	for (var i = 0; i < this.childNodes.length; i++) {
		str += this.childNodes[i].toString(i,this.childNodes.length);
	}
	str += "</div>";
	this.plusIcon = ((this.parentNode._last)?image_lplus:image_tplus);
	this.minusIcon = ((this.parentNode._last)?image_lminus:image_tminus);
	return str;
}


/*

Run this script through one of the perl pod2* commands to generate API
docuementation.

=head1 NAME

index_tree.js

=head1 VERSION

 $Source: /home/woods/src/mod/RCS/index_tree.js,v $
 $Date: 2002/09/04 13:02:29 $
 $Revision: 0.10 $

=head1 SYNOPSIS

 // define the new tree (this could be in a separate file, if it's the same
 // for the whole website)
 tree = new IndexTree(target, 'Home', url, 
    [ 'folder', 'Breeds', url, 
        [ 'file', 'German Shepherd', url,
            [ 'topic', 'Behavior', url,
                [ 'topic', 'With Family', url, ],
                [ 'topic', 'With Strangers', url, ],
            ],
            [ 'topic', 'Feeding', url ],
        ],
        [ 'file', 'Bouvier', url ],
        [ 'file', 'Schnauzer', url ],
        [ 'file', 'Shih Tzu', url ],
    ],
    [ 'folder', 'Training', url ],      // empty folder
 );
        
 // set the current file (should be identical to what it was above)
 tree.this_file = url;
 
 // write the tree to html. if current_file is set, 
 // write() will highlight the correct topic based on document.url.
 tree.Display();

=head1 DESCRIPTION

index_tree.js defines javascript classes to generate an expandable/collapsable
navigation tree, similar to Windows Explorer's. This is used as part of the MOD
package.

=cut

*/

/*

=head1 PUBLIC INTERFACE


=head2 class IndexTree

An instance of this class represents the root of an index tree. IndexNodes are
subsequently added to the root.

=over

=item IndexTree()

I<Description:> constructor. creates a new tree and defines its entire structure.

I<Arguments:>

=over

=item target

If the web page uses frames, you can use this field to specify the target for the
links. Otherwise, use the empty string.

=item name

The name that should be displayed for the root node. common names are 'Home'
or  'Site Contents'.

=item url

The url to load if a user clicks on the root of the tree

=item tree

The definition of the tree. This is done using lists.  

=back

I<Options:> none

I<Returns:> nothing

=cut

*/

function IndexTree(target, url_prefix, name, url) {
    if (arguments.length < 5) {
        alert('index_tree.js: too few arguments to IndexTree constructor');
    }

    // determine whether this is Netscape 4 or not
    var agent           = navigator.userAgent;
    var supports_layers = document.layers ? 1 : 0;
    var supports_dom    = document.getElementById ? 1 : 0;
    var is_hotjava      = agent.toLowerCase().indexOf('hotjava') != -1 ? 1 : 0;
    this.ns4            = supports_layers && !supports_dom && !is_hotjava;
    var ns4             = this.ns4;

    // initialize the tree
    var cookie_life_in_days = 60;
    if (this.ns4) {
        // use jstreemenu. not preferred, but it works on ns4.
        treemenu_cookie = new Cookie(document, 'jstreemenu', 24 * cookie_life_in_days, url_prefix);
        treemenu_cookie.load();
        
        var jstreemenuitems = new Array();
        var level = 0;
    } else {
        // use xtree
        treeHandler.cookie = new Cookie(document, 'xtree', 24 * cookie_life_in_days, url_prefix);
        treeHandler.cookie.load();
        
        this.xtree = new Tree(name, url);
        var curParent = this.xtree;
    }
        
    // function to recursively process a branch in the argument list,
    // building the tree in the process. 
    var num_branches = 0;
    function addBranch(branch) {
        num_branches++;
        if (branch == undefined) 
            alert('branch number ' + num_branches + ' is undefined');
            
        if (branch.length < 3) {
            alert('index_tree.js: too few arguments in branch specification (' + branch.length + ')');
        }
        var type = branch[0];
        var name = branch[1];
        var url  = branch[2];
        
        // add the current node
        if (ns4) {
            jstreemenuitems.push([level, name, url, target, type]);
        } else {
            var newItem = new TreeItem(name, url, curParent, type);
        }

        if (branch.length == 3) return;      // leaf node
        
        // recursively process any branches that belong to this node
        if (ns4) {
            level++;
        } else {
            var oldParent = curParent;
            curParent = newItem;
        }
        for (var i = 3; i < branch.length; i++) {
            addBranch(branch[i]);
        }
        if (ns4) {
            level--;
        } else {
            curParent = oldParent;
        }
        return;
    }

    // recursively build the entire tree. 
    for (var i = 4; i < arguments.length; i++) {
        addBranch(arguments[i]);
    }
    
    // jstreemenu needs one last step to actually build the tree
    if (this.ns4) {
        this.jstreemenu = buildMenu(jstreemenuitems);
    }
    
    return;
}

/*

=item currentFileUrl

A property that defines the url of the currently displayed filename. This should
exactly match the URL that was given to the node that's being referenced. When
the tree writes the HTML, it will highlight the given page. If C<document.url>
indicates that an a_name has been selected as well, it will look for that topic
and highlight it instead.

=cut

*/

IndexTree.prototype.ExpandURL = function(url) {
    if (this.ns4) {
        return this.jstreemenu.ExpandURL(url);
    } else {
        return this.xtree.expandURL(url);
    }
}


/*

=item Display()

Write the given tree to the current document.

=cut

*/

IndexTree.prototype.Display = function() {
    if (this.ns4) {
        return this.jstreemenu.Display();
    } else {
        return document.write(this.xtree);
    }
}


/*

=head1 SEE ALSO

Minimalist Online Documentation (MOD).
    http://www.emsei.psu.edu/~woods/mod/

=head1 AUTHOR

Scott A. Woods C<scott@westarete.com>

=cut

*/
