function LinksAjaxBreadCrumbs(className, containerID, reverse, thisPage, linkImages) {
	this.className = className;
	this.containerID = containerID;
	this.reverse = reverse;
	this.thisPage = thisPage;
	this.linkImages = linkImages;
	Utilities.register(this);
}
LinksAjaxBreadCrumbs.prototype = {
	onPageLoad : function() {
		var c, e;
		e = (this.container = document.getElementById(this.containerID)).firstChild;
		while (e) {
			if (e.nodeType == 1) {
				this.baseLink = e;
				break;
			}
			e = e.nextSibling;
		}
	},
	clearLinks : function() {
		var t = this, c = t.container, r = t.reverse, e, p = c.parentNode;
		while(e = r ? c.previousSibling : c.nextSibling) {
			p.removeChild(e);
		}
	},
	setBaseLink : function(link, isSelected) {
		var t = this, ble = t.baseLink, newe, li, e;
		t.clearLinks();
		if (ble.nodeName.toLowerCase != "a") {
			ble.parentNode.replaceChild(newe = document.createElement("a"), ble);
			newe.className = ble.className;
			newe.id = ble.id;
			ble.id = "";
			// Copy children rather than innerHTML because innerHTML copy creates new elements
			while(e = ble.firstChild) newe.appendChild(e);
			ble = t.baseLink = newe;
		}
		ble.setAttribute("href", link);		
		ble.className = t.className + (isSelected ? 'SelectedLink' : 'Link');
		if (li = t.linkImages) {
			ble.setAttribute('onmouseover', li + ".mouseOver('" + t.thisPage + "')");
			ble.setAttribute('onmouseout', li + ".mouseOut('" + t.thisPage + "')");
		}
	},
	addCrumb : function(link, title, isSelected, page, ext) {
		var t = this, le, be = t.container, p = be.parentNode, li = t.linkImages, lio = window[li];
		le = document.createElement("a");
		le = t.reverse ? p.insertBefore(le, p.firstChild) : p.appendChild(le);
		le.className = t.className + (isSelected ? 'SelectedLink' : 'Link');
		le.setAttribute("href", link);
		if (page) {
			// have to build the image and rollovers
			lio.addImage(le, page, ext, title);
			le.setAttribute('onmouseover', li + ".mouseOver('" + page + "')");
			le.setAttribute('onmouseout', li + ".mouseOut('" + page + "')");
		} else {
			le.innerHTML = title;
		}
	},
	addCrumbFromAjax : function(link, isSelected, e) {
		var t = this, title = xdom_GetData(e), v, page, ext=null;
		if (page = e.getAttribute('link_page')) {
			ext = e.getAttribute('link_ext');
		}
		t.addCrumb(link, title, isSelected, page, ext);
	}
}
function LinkImagesEngine(className) {
	this.images = new Object();
	this.rollovers = new Object();
	this.imagesrc = new Object();
	this.ext = null;
	this.path = "/images/";
	this.className = className;
	Utilities.register(this);
}
LinkImagesEngine.prototype = {
	getRollovers : function(pages, ext, path) {
		var t = this, rslt, pg, i;
		t.ext = ext;
		t.pages = pages;
		if (path) this.path = path;
		for(i=0;i<pages.length; i++) {
			pg = pages[i];
			rslt = new Image();
			rslt.src = t.path + pg + '_over.' + ext;
			t.rollovers[pg] = rslt;
		}
	},
	onPageLoad : function() {
		var t = this, pages = t.pages, i, pg, c = t.className, img;
		if (!pages) return;
		for(i=0;i<pages.length; i++) {
			pg = pages[i];
			img = document.getElementById(pg);
			if (!img) img = document.getElementById(c + pg);
			t.images[pg] = img;
			t.images[c + pg] = img;
			if (img) {
				t.imagesrc[pg] = img.src;
				t.imagesrc[c + pg] = img.src;
			}
		}
	},
	mouseOver : function(pg) {
		var img = this.images[pg];
		if (img) img.src = this.path + pg + '_over.' + this.ext;
		var ug = img;
	},
	mouseOut : function(pg) {
		var img = this.images[pg];
		if (img) img.src = this.imagesrc[pg]; //this.path + pg + '.' + this.ext;
		var ug = img;
	},
	addImage : function(parent, pg, ext, title) {
		var t = this, img, src, rslt;
		parent.appendChild(img = new Image());
		src = t.path + pg + '.' + ext;
		img.setAttribute("src", src);
		img.setAttribute("alt", title);
		t.images[pg] = img;
		t.imagesrc[pg] = src;
		rslt = new Image();
		rslt.src = t.path + pg + '_over.' + ext;
		t.rollovers[pg] = rslt
		return img;
	}
}
LinkImages = new LinkImagesEngine();
Number.prototype.NaN0=function(){return isNaN(this)?0:this;}
function LinksPopupMgmtEngine() { 
	this.Sets = new Array();
	this.TimeoutControl = new Array();
	Utilities.register(this);
}
LinksPopupMgmtEngine.prototype = {
	onPageLoad : function() {
		var e, i, set;
		for(i=0; i<LinksPopupMgmt.Sets.length; i++) {
			set = LinksPopupMgmt.Sets[i];
			// The root link should normally always have children but...
			if (!(e = document.getElementById(set.cid))) 
				e = document.getElementById(set.className);
			if (set.children) new LinksPopupSet(set, e, null);
		}
	},
	fixMinWidthForIE : function() {
		var sty, i, e, els, mw, shm;
		if (Utilities.Opera) {
			els=document.getElementsByTagName("*");
			for(i=0; i<els.length; i++) {
				e = els[i];
				sty = window.getComputedStyle(e, "");
				mw = sty.getPropertyValue('min-width');
				if(mw && mw != 'auto' && mw != '0px'){
					shm = document.createElement("DIV");
					shm.style.cssText = 'margin:0 !important; padding:0 !important; border:0 !important; line-height:0 !important; height:0 !important; BACKGROUND:RED;';
					shm.style.width = mw;
					shm.appendChild(document.createElement("span"));
					e.appendChild(shm);
				}
			}
			return;		
		}
		try{
			if(!document.body.currentStyle){return} //IE only
		} catch(e){return} 
		els=document.getElementsByTagName("*");
		for(i=0; i<els.length; i++) {
			e = els[i];
			sty = e.currentStyle;
			mw = (sty.minWidth) ? sty.minWidth : sty.getAttribute("min-width"); //IE7 : IE6
			if(mw && mw != 'auto'){
				shm = document.createElement("DIV");
				shm.style.cssText = 'margin:0 !important; padding:0 !important; border:0 !important; line-height:0 !important; height:0 !important; BACKGROUND:RED;';
				shm.style.width = mw;
				shm.appendChild(document.createElement("&nbsp;"));
				if(e.canHaveChildren) {
					e.appendChild(shm);
				}
			}
		}		
	},
	addSet : function(setName, set) {
		this.Sets.push(set);
	},
	/**
	 * Loads the link set specified by set.  This could be the "root" set or a link element that 
	 * has a set of children.
	 */
	loadSet : function(set, parent) {
		
	}
}
LinksPopupMgmt = new LinksPopupMgmtEngine();
/**
 * The LinksPopupSet class manages one set of popup links.  There is also one special instance for the "root"
 * and it's child links.  Where this really comes into play are the children of each of the root children or
 * main links.  Every link, including grandchildren that have sub-links will have an instance of this class.
 * 
 * @param set - the link information object from the server for this link set.
 * @param container - the "div" container in which the main links are contained and which dropdown links will be parented
 * @param pe - the actual parent link element (null if this is the root set)
 */
function LinksPopupSet(set, container, pe) {
	var yoff = 0;
	this.set = set;
	this.parentElement = pe;
	this.container = container;
	set.popup = this;
	/*
	 * These properties govern how quickly the mousover event triggers the visibility
	 * of the sublinks and how quickly the mouseout event triggers the invisibility.
	 */
	this.overDelay = set.over;
	this.outDelay = set.out;
	this.overTimerID = null;
	this.outTimerID = null;
	
	/*
	 * Setup functions that will be used to call methods for event hadling
	 */
	var t = this;
	var mouseout = function(event){t.mouseout(event);};
	var mouseover = function(event){t.mouseover(event);};
	
	if (pe) {
		/* 
		 * If this is not the root set we will get the positioning information from the parent link element
		 * plus offsets and pads from the server to form a starting location where this link set will be
		 * built.
		 */		
		var up = set.up, bubbleLeft = (set.dir == 'left');
		var pos = this.pos = pe.pos;
		var xoff = this.xoff = (set.xanc == "left" ? pos.left : pos.right) + set.xoff;
		var ypad = up ? -set.ypad : set.ypad;
		yoff = this.yoff = (set.yanc == "top" ? pos.top : pos.bottom) + (up ? -set.yoff : set.yoff);
		
		/*
		 * We want the parent element to handle mouse events on this set but also want to notify the
		 * parent set if being handled there.  We will do this by saving the parent handlers if they exist
		 * and calling them after handling the event here.
		 */
		this.onmouseover = pe.onmouseover ? pe.onmouseover : null;
		this.onmouseout = pe.onmouseout ? pe.onmouseout : null;
		pe.onmouseover = mouseover;
		pe.onmouseout = mouseout;
		this.visible = false;
	}
	var csets = this.csets = set.children;
	var first = true, e, l, maxw = 0;
	var dmaxw = set.maxw ? set.maxw : 0;
	
	/*
	 * Go through each child set.  If this is the root set we only find the already existing link element
	 * by its ID, otherwise we will build link elements for each of the children and position them where 
	 * they will be when fully expanded.
	 */
	for (var i=0; i<csets.length; i++) {
		var cset = csets[i];
		if (pe == null) {
			// This is the top level set and elements should already exist
			e = document.getElementById(cset.id);
			if (!e) return this;
			e.pos = Utilities.getAbsPosition(e, "Anchor");
		} else {
			e = document.createElement("div");
			e.className = cset.className;
			e.id = cset.id;
			e.style.visibility = "hidden";
			e.style.position = "absolute";
			if (dmaxw) e.style.width = dmaxw + 'px';
			container.appendChild(e);
			if (cset.url) {
				l = document.createElement("a");
				l.setAttribute("href", cset.url);
			} else {
				l = document.createElement("span");
			}
			l.innerHTML = cset.title;
			l.className = cset.className + 'Link';
			l.style.display = "block";
			e.appendChild(l);
			var h = parseInt(e.offsetHeight);
			var w = parseInt(e.offsetWidth);
			maxw = (w > maxw) ? w : maxw;
			if (first && up) yoff -= h;
			e.style.top = yoff + 'px';
			e.style.left = xoff + 'px';
			//e.pos = {left:xoff, top:yoff, right:xoff + w, bottom:yoff + h};
			e.onmouseover = mouseover;
			e.onmouseout = mouseout;
			yoff += (up ? -h : h) + ypad;
			if (first) {
				var firste = e;
				var padding = $(e).css('paddingRight');
			}
			first = false;
		}
		cset.element = e;
		cset.linkElement = l;
		
		e.linkObject = cset;
		if (!pe && cset.children) new LinksPopupSet(cset, container, e);
	}
	if (!pe) return this;
	if (dmaxw) maxw = dmaxw;
	
	// For all popup links in this set, set all widths to the max
	// and adjust left if we are bubbling left
	for (var i=0; i<csets.length; i++) {
		cset = csets[i];
		e = cset.element;
		e.style.width = maxw + 'px';
		cset.linkElement.style.width = maxw + 'px';
		e.pos = Utilities.getAbsPosition(e, "Anchor");
		if (bubbleLeft) {
			cset.element.style.left = (xoff - maxw) + 'px';
			cset.element.pos.left -= maxw;
		}
	}
	/* Create a gap element if the dropdown stack is separated from
	 * the parent element too far from the parent element.  We do this
	 * to keep the dropdown menu from disappearing if the user is slow
	 * in moving from the parent link to the dropdown stack
	 */
	if (firste) {
		var gapLeft = false, gapTop, gapRight, gapBottom;
		if (firste.pos.right < pos.left) {
			// Gap on Left - fill full vertical space on left
			gapLeft = firste.pos.right;
			gapRight = pos.left;
			gapTop = Math.min(firste.pos.top, pos.top);
			gapBottom = Math.max(firste.pos.bottom, pos.bottom);
		} else if (firste.pos.left > pos.right){
			// Gap on Right - fill full vertical space on right
			gapLeft = pos.right;
			gapRight = firste.pos.left;
			gapTop = Math.min(firste.pos.top, pos.top);
			gapBottom = Math.max(firste.pos.bottom, pos.bottom);
		} else if (firste.pos.top > pos.bottom) {
			// Gap on Bottom - fill full horizontal space below
			gapTop = pos.bottom;
			gapBottom = firste.pos.top;
			gapLeft = Math.min(firste.pos.left, pos.left);
			gapRight = Math.max(firste.pos.right, pos.right);
		} else if (firste.pos.bottom < pos.top) {
			// Gap on Top - fill full horizontal space above
			gapTop = firste.pos.bottom;
			gapBottom = pos.top;
			gapLeft  = Math.min(firste.pos.left, pos.left);
			gapRight = Math.max(firste.pos.right, pos.right);
		}
		if (gapLeft !== false) {
			e = document.createElement("div");
			e.style.visibility = "hidden";
			e.style.position = "absolute";
			e.style.top = gapTop + 'px';
			e.style.left = gapLeft + 'px';
			e.style.width = (gapRight - gapLeft) + 'px';
			e.style.height = (gapBottom - gapTop) + 'px';
			e.onmouseover = mouseover;
			e.onmouseout = mouseout;
			container.appendChild(e);
			firste.gap = e;
		}
	}
	
	// Now, finally look for children
	for (var i=0; i<csets.length; i++) {
		cset = csets[i];
		if (cset.children) new LinksPopupSet(cset, container, cset.element);
	}
}
LinksPopupSet.prototype = {
	/**
	 * mouseover is invoked whenever the mouse has crossed over the parent or
	 * any child element in this set.  
	 */
	mouseover : function(event) {
		// Signal parent set if needed.
		if (this.onmouseover) this.onmouseover(event);
		
		// Are we re-entering while timing out on a mouseout?
		if (this.outTimerID) {
			clearInterval(this.outTimerID);
			this.outTimerID = null;
		}
		
		// Just exit if we are already waiting for the overTimer to pop
		if (this.overTimerID) return;
		
		// None of the above.  Set a timer to trigger the visibility after a delay.
		var t = this;	
		this.overTimerID = setInterval(function(){t.overTimer();}, this.overDelay);
	},
	mouseout : function(event) {
		// signal parent set if needed.
		if(this.onmouseout) this.onmouseout(event);
		
		// Have we exited while timing out on a mouseover?
		if (this.overTimerID) {
			clearInterval(this.overTimerID);
			this.overTimerID = null;
		}
		
		// Exit if we are already wating for the outTimer to pop (this probably can't happen)
		if (this.outTimerID) return;
		
		// None of the above.  Set a timer to trigger the invisibilty after a delay.
		var t = this;	
		this.outTimerID = setInterval(function(){t.outTimer();}, this.outDelay);
		
	},
	overTimer : function() {
		if (this.set.bbl) this.bubbleVisible();
		else this.makeVisible();
	},
	outTimer : function() {
		this.makeInvisible();
	},
	makeVisible : function() {
		if (this.visible) return true;
		for (var i=0; i<this.csets.length; i++) {
			if (this.csets[i]) {
				var e = this.csets[i].element;
				e.style.visibility = 'visible';
				if (e.gap) e.gap.style.visibility = 'visible';
			}
		}
		this.visible = true;
		return false;
	},
	bubbleVisible : function() {
		if (this.visible || this.bubbling) return true;
		this.bubbling = true;
		this.index = 0;
		var t = this;
		this.bubbleTimer = setInterval(function(){t.bubbleNext();}, this.set.bbl);
		this.bubbleNext();
		return false;
	},
	bubbleNext : function() {
		var c = this.csets;
		var i = this.index++;
		if (c[i]) {
			var e = c[i].element;
			e.style.visibility = 'visible';
			if (e.gap) e.gap.style.visibility = 'visible';
		}
		if (this.index >= c.length) {
			clearInterval(this.bubbleTimer);
			this.bubbling = false;
			this.visible = true;
		}
	},
	makeInvisible : function() {
		if (!this.visible) return true;
		for (var i=0; i<this.csets.length; i++) {
			if (this.csets[i]) {
				var e = this.csets[i].element;
				e.style.visibility = 'hidden';
				if (e.gap) e.gap.style.visibility = 'hidden';
			}
		}
		this.visible = false;
		return false;
	}
}
