/// Menu
//
//
Menu.prototype.getMenuItem = Menu_getMenuItem;
Menu.prototype.displayMenu = Menu_displayMenu;
Menu.prototype.getMouseY = Menu_getMouseY;
Menu.prototype.offMenu = Menu_offMenu;
Menu.prototype.setBounds = Menu_setBounds;
Menu.prototype.isInBounds = Menu_isInBounds;
Menu.prototype.collapse = Menu_collapse;
Menu.prototype.init = Menu_init;
Menu.prototype.setProperty = Menu_setProperty;
Menu.prototype._getChildItem = Menu_getChildItem;
Menu.prototype._initLayer = Menu_initLayer;
Menu.prototype._getBottomPosOffset = Menu_getBottomPosOffset;


// Menu constructor
function Menu(name, dataObj) {

	this.name = name;// used for recursion
	this.data = dataObj;// the object literal from .js file
	
	//	config, set these using setProperty()
	this.layerNamePrefix = "";
	this.linkCssClass = "";
	this.offsetH = new Array();// offset from edge of parent menu
	this.offsetV = 0;// offset from mouse position
	this.itemHeight = 0;// height of menu item in px
	this.menuHeightOff = 0;// additional offset for menu height
	this.scrSafeArea = 0;// area of screen edge to be avoided by menu
	this.topMenuBounds = new Array(0,0,0,0);
	this.wraplength = new Array();
	this.linkHref = "#";
	// end config
	
	this.Html = new HtmlLink();// Html handling object
	this.bounds = new Array();// left, top, right, bottom pos of menu
	this.width = new Array();// widths of menus
	this.layer = new Array();// layer objects
	
	// state
	this.nameStack = new Array();
	this.currYpos = 0;
	this.currXpos = 0;
	this.active = new Array();
	this.mouseX = 0;
	this.mouseY = 0;
	
	return true;
}

// set a Menu object property
function Menu_setProperty(name, value) {
	this[name] = value;
}

// bind layers to Menu and assign event handlers
// called with onload
function Menu_init() {
	this.depth = this.width.length;
	
	// set bounding of top menu
	this.setBounds(0,this.topMenuBounds[0],
		this.topMenuBounds[2],
		this.topMenuBounds[3],
		this.topMenuBounds[1]);
	
	// create sub menu objects
	for(var i=1; i<this.depth; i++) {
		this._initLayer(i);
	}
	
	// assign event handlers for mousemove
	if(!handleMousemove)
		return LayerObj.error("handleMousemove","is not defined");

	if(UA.NS) window.onmousemove = handleMousemove;
	if(UA.IE) document.onmousemove = handleMousemove;
	// capture move events
	if(UA.NS) window.captureEvents(Event.MOUSEMOVE);
}


// initialise menu layers
function Menu_initLayer(i) {
	this.active = false;
	// if no layerObj passed then create one
	this.layer[i] = new LayerObj(this.layerNamePrefix+(i));
	if(!this.layer[i]) {
		LayerObj.error(
			this.layerNamePrefix+(i),"create sub menu layers failed");
	}
	// set to zero if undefined
	if(this.offsetH.length < i)
		this.offsetH[i] = 0;
	
	// set bounds relative to parent
	this.setBounds(i,
		this.layer[i].getTop(),
		this.layer[i].getTop(),
		this.bounds[i-1].right + this.offsetH[i],
		this.bounds[i-1].right + this.offsetH[i] + this.width[i]);
}



// set bounds for a menu, absolute coords t,b,l,r
function Menu_setBounds(level, top, bottom, left, right) {
	if(!this.bounds[level]) this.bounds[level] = new Array();
	this.bounds[level].top = top;
	this.bounds[level].bottom = bottom;
	
		this.bounds[level].left = left;
	
		this.bounds[level].right = right;
}

// called from link event
//	args: submenu name, event object
function Menu_displayMenu(level, name, e) {
	if(name == "null") {
		// is a leaf
		if(level < this.depth) {
			// hide remaining child menu if leaf
			this.active[level] = false;
			this.layer[level].hide();
		}
		return false;
	}
	// find a submenu in the tree
	var currItem = this.getMenuItem(level,name);
	
	if(currItem == null) return false;
	var currEl = currItem.el;
	
	// move the layer to the current link position
	
	this.currYpos = this.getMouseY(e) + this.offsetV;
	if(level > this.layer.length-1) {
		alert(level+ " exceeds the number of menu layers")
		return false;
	}
	
	// create the menu HTML
	var menuHtml = "";
	var wrapCount = 0;
	var el;
	var childLevel = level+1;
	var passedName;
	var displayName;
	var href;
	var type;
	for(var i=0; i<currEl.length; i++) {
		el = currEl[i];
		
		passedName = el.name;
		type = "Category";
		if(typeof el.el == "undefined") {
			// child is a leaf
			passedName = "null";
			type = "LeafCategory";
		}
		nameWrapObj = wrapHtmlString(el.name, this.wraplength[level]);
		wrapCount += nameWrapObj[1];
		
		if (el.name == "Software Downloads")
		{
			href = 'javascript:dsgload()';
		}
		else
		{
			href = getBVUrlString(this.linkHref, el.src, type);
		}


		menuHtml += this.Html.getLine(nameWrapObj[0], href,
			this.linkCssClass, null,
			this.name+".displayMenu("+childLevel+",'"+passedName+"',event)",
			this.name+".offMenu("+childLevel+")"
		);
	}
	
	// create display box
	menuHtml = this.Html.wrapBox(menuHtml,
		this.width[level],
		(wrapCount * this.itemHeight + this.menuHeightOff)
	);
	

	// adjust mouse position for scrolling
	this.currYpos += (UA.NS)? 0: document.body.scrollTop;
	
	// set the current top and bottom of layer
	this.bounds[level].top = this.currYpos;
	this.bounds[level].bottom = 
		wrapCount * this.itemHeight + this.menuHeightOff + this.currYpos;

	var limitBot = 0;
	// limit downward spread of menus if bottomLimit set
	if(typeof this.bottomLimit != "undefined") {
		limitBot = this._getBottomPosOffset(level,this.bottomLimit);
	}
	
	// if menu sits below bottom of window then move up
	var scrBot = this._getBottomPosOffset(level,
		this.layer[level].getWinHeight() - this.scrSafeArea);
	
	var bottomPosOffset = (scrBot > limitBot)? scrBot: limitBot;
	
	this.currYpos -= bottomPosOffset;
	this.bounds[level].top = this.currYpos;
	this.bounds[level].bottom -= bottomPosOffset;

	
	
	this.layer[level].moveTo(
		this.bounds[level].left,
		this.currYpos);
		
	this.Html.outLine(menuHtml,this.layer[level]);
	this.layer[level].show();
	this.active[level] = true;
	return false;
}

// return the child Tree element for name, or null if leaf
function Menu_getMenuItem(level, name) {
	// put breadcrumb in the stack
	this.nameStack[level-1] = name;
	this.nameStack.length = level;
	
	var currItem = this.data;
	// go through each level
	for(var l=0; l<this.nameStack.length; l++) {
		// get element
		currItem = this._getChildItem(currItem, l);
	}
	return currItem;
}

function Menu_getChildItem(itemObj, l) {
	// bail if we have a leaf
	if(typeof itemObj == "undefined")
		return null;
		
	// look for name in element
	for(var i in itemObj.el) {
		if(itemObj.el[i].name == this.nameStack[l]) {
			// got the item
			return itemObj.el[i];
		}
	}
	return null;
}


// return Y pos of mouse
function Menu_getMouseY(e) {
	if(UA.IE) e = window.event
	return (UA.NS)? e.pageY: e.clientY;
}

// called if mouse moves off link
function Menu_offMenu(level) {
	this.active[level] = false;
	return true;
}


// collapse menus
// called with a mousemove event, passing an event e
// wrapper function is assigned as event handler so
// that this method can be bound to the Menu class
function Menu_collapse(e)
{
	if(UA.IE) e = window.event;
	var mouseX; var mouseY
	if (UA.NS)
	{
		mouseX = e.pageX;
		mouseY = e.pageY;
	}
	if (UA.IE)
	{
		mouseX = e.clientX + document.body.scrollLeft;
		mouseY = e.clientY + document.body.scrollTop;
	}
	var closeDepth = 0;
	// climb the menu tree closing the unwanted
	for(var i = this.depth-1; i>-1; i--)
	{
		// if menu is not active and mouse is not in menu or parent menu
		if(!this.active[i] && !this.isInBounds(i, mouseX, mouseY) &&
			i > 0 &&
			!this.isInBounds(i-1, mouseX, mouseY))
		{
			this.layer[i].hide();
		}
		else break;
	}
	// allow event to route
	return true;
}

// check if xy is within a menu
function Menu_isInBounds(l, x, y) {
	if(x < this.bounds[l].left 
		|| x > this.bounds[l].right)
	{
			return false;
	}
	if(y < this.bounds[l].top 
		|| y > this.bounds[l].bottom)
	{
			return false;
	}
			
	return true;
}


// return adjust value to position menu within bottomLimit
function Menu_getBottomPosOffset(level, bottomLimit) {
	var safePosOffset = this.bounds[level].bottom - bottomLimit;
	return (safePosOffset > -1)? safePosOffset: 0;
}


/// HtmlLink
//
//


//	holds markup and structure to build menu line
HtmlLink.prototype.getLine = HtmlLink_getLine;
HtmlLink.prototype.getAttr = HtmlLink_getAttr;
HtmlLink.prototype.wrap = HtmlLink_wrap;
HtmlLink.prototype.outLine = HtmlLink_outLine;
HtmlLink.prototype.wrapBox = HtmlLink_wrapBox;

// HtmlLink constructor
function HtmlLink() {
	// config
	this.boxCss = "hMenuBox";
	this.TableOuterCss = "hMenuTableOuter";
	this.TableInnerCss = "hMenuTableInner";
	this.closeLink = '</A>';
	this.endLine = '<BR>\n';
}

// Wrap content with an html display box
function HtmlLink_wrapBox(content, w, h) {
	var ret = "";
	if(UA.IE) {
		// output a div
		ret += '<DIV CLASS="'+ this.boxCss +'" '+'STYLE="width:'+ w +'; height:'+ h +';">';
		ret += content +'</DIV>';
	} else {
		// output a table
		ret += '<TABLE WIDTH="'+ w;
		ret += '" BORDER="0" CELLSPACING="0" CELLPADDING="1" CLASS="';
		ret += this.TableOuterCss +'"><TR><TD>';
		ret += '<TABLE WIDTH="'+ (w-2);
		ret += '" BORDER="0" CELLSPACING="0" CELLPADDING="5" CLASS="';
		ret += this.TableInnerCss +'"><TR><TD>';
		ret +=  content + '</TD></TR></TABLE></TD></TR></TABLE>';
	}
	return ret;
}

// Output HTML to a layer
function HtmlLink_outLine(content,layerObj) {
	if (UA.NS) {
		layerObj.layer.document.open();
		layerObj.layer.document.write(content);
		layerObj.layer.document.close();
	}
	else if (UA.IE) {
		layerObj.layer.innerHTML = content;
	}
}
// return a line of the menu
function HtmlLink_getLine(lText,lHref,lClass,lTarget,lMouseOver,lMouseOut,preElem, postElem) {
	var linkStr = "";
	preElem = (preElem)? preElem: "";
	postElem = (postElem)? postElem: "";
	// <A> tag
	linkStr += this.getAttr('A HREF=',lHref);
	linkStr += this.getAttr(' CLASS=',lClass);
	linkStr += this.getAttr(' TARGET=',lTarget);
	linkStr += this.getAttr(' ONMOUSEOVER=',lMouseOver);
	linkStr += this.getAttr(' ONMOUSEOUT=',lMouseOut);
	linkStr = this.wrap(linkStr,'<>');
	// preElem<A>lText</A>
	linkStr = preElem + linkStr;
	linkStr += lText + this.closeLink;
	// preElem<A>lText</A>postElem<BR>\n
	linkStr += postElem + this.endLine;
	return linkStr;
}
// return an attribute string
function HtmlLink_getAttr(attrName, attrStr) {
	if(!attrStr) return '';
	return attrName + this.wrap(attrStr,'"');
}
// wrap str with delim, eg: <str>
function HtmlLink_wrap(str, delim) {
	if (delim != "<>") return delim + str + delim;
	return "<"+ str +">";
}

function wrapHtmlString(str, lineLen) {
	var eolStr = "<BR>\n";
	var c = 0;
	var lines = [""];
	var words = str.split(/\s+/);
	var separator;
	for(var i=0; i<words.length; i++) {
		// if over wrap length
		if(lines[c].length + words[i].length > lineLen) {
			c++;
			lines[c] = "";
		}
		lines[c] += words[i] + " ";
	}
	return [lines.join(eolStr), lines.length];
}

// convert an oid into a url
// type: Category | LeafCategory
function getBVUrlString(urlPath, oid, type) {

	ret = urlPath;
	ret += oid;
	
	return ret
}


// EOF


