
//
// +---------------------------------------------------------------------+
// | BEYOND-RUNNING.COM                                                  |
// +---------------------------------------------------------------------+
// | beet.js                                                             |
// | beet menu js functions                                              |
// |                                                                     |
// | Dtek Digital Media, dtek.net                                        |
// | 2006.01.19                                                          |
// |                                                                     |
// +---------------------------------------------------------------------+
//

/*  
---------------------------------------------------------------------
beet is a simple set of scripts for creating DHTML menus.

Copyright (C) 2004 Daniel Kahn Gillmor

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program (in the file "COPYING"); if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.

or look for the GPL online at http://www.fsf.org/copyleft/gpl.html
---------------------------------------------------------------------
*/

// how to use:

// For each menubaritem, call beetInitMenu() directly from within your
// html page somewhere after the menubaritem has been declared.  (or
// in the page's onLoad function)


// this stores all the beet menubaritems so that we can collapse any
// open menus when a new one gets expanded
var beetmenubaritems = new Array();



// timeout is in milliseconds...
function beetInitMenu(menubaritem, position, values, overimage, timeout) {
	
	if (menubaritem.obj.className != "beetmenubaritem") {
		alert("you can't actually call this function on anything but an item with class beetmenubaritem");
		return;
	}
	// the less strict method would be to initialize an element
        // with any class, but just change the element's class to
        // beetmenubaritem within this function.
        //   but i think that could lead to confusion: "i made
        // that thing class whatzit, but it's not showing up with the right
        // style!".  this is a somewhat fascist method of avoiding
        // that confusion.


	if (typeof timeout == "undefined") var timeout = 1000;

	// we'll replace the current element with this span...
	var foo = document.createElement("span");
	while (menubaritem.obj.hasChildNodes()) {
		foo.appendChild(menubaritem.obj.removeChild(menubaritem.obj.firstChild));
	}
	
	// we should also store the menubaritem's href attribute, if it was an <a> element.
	if (menubaritem.obj.tagName.toLowerCase() == "a") { 
		foo.linktarget = menubaritem.obj.href;
	} else {
		foo.linktarget = "";
	}

	foo.id = menubaritem.obj.id;
	foo.className = menubaritem.obj.className;
	menubaritem.obj.parentNode.replaceChild(foo, menubaritem.obj);
	

	foo.onmouseover = beetMenuBarItemMouseover;
	foo.onmouseout = beetMenuBarItemMouseout;
	foo.onclick = beetMenuBarItemClick;
	foo.menuitems = values;
	foo.menupos = position;
	if (overimage && (overimage != ""))
		foo.overimage = overimage;
	foo.beettimeoutduration = timeout;
	foo.menu = null;
	foo.timeout = null;
	beetmenubaritems[beetmenubaritems.length] = foo;
}

function beetMenuBarItemMouseover() {
	beetUpMenuHere(new getObj(this.id));
}
function beetMenuBarItemMouseout() {
	beetResetTimeout(this);
}
function beetMenuBarItemClick() {
	if (this.linktarget != "") {
		location = this.linktarget;
	} else {
		beetToggleMenuHere(new getObj(this.id));
		return false;
	}
}

function beetToggleMenuHere(foo) {
	if (foo.obj.menu == null) {
		beetUpMenuHere(foo);
	} else {
		beetDownMenuHere(foo);
	}
}

function beetDownMenu() {
	beetDownMenuHere(new getObj(this.id));
}

function beetClickMenuItem() {
 	beetClickMenuItemHere(this);
}
function beetHighlightMenuItem() {
	beetHighlightMenuItemHere(this);
}
function beetDeHighlightMenuItem() {
	beetDeHighlightMenuItemHere(this);
}
function beetDownMenuByName(nn) {
	beetDownMenuHere(new getObj(nn));
}

// brings down all beet menus in the array specified.  if you don't
// specify an array it uses the global array of beetmenubaritems which
// should contain all of them on the page.
function beetSuppressMenus(menubaritems) {
	if (typeof menubaritems == "undefined") 
		var menubaritems = beetmenubaritems;
	for (var ix = 0; ix < menubaritems.length; ix++ ) {
		beetDownMenuHere(new getObj(menubaritems[ix].id));
	}
}

function beetSuppressMenusExcept(menubaritems, exception) {
	for (var ix = 0; ix < menubaritems.length; ix++ ) {
		if (menubaritems[ix] != exception) {
			beetDownMenuHere(new getObj(menubaritems[ix].id));
		}
	}
}

function beetSetDescendantAttributeOfTagName(obj, tag, attr, val) {
	//if val is not present, the we will reset the old attr
	//which was stored in "old"+attr.
//	var debugstring = "";	

	//enumerate the children
	if (obj.hasChildNodes()) {
		for (var ix = 0; ix < obj.childNodes.length; ix++) {
		
		// if a child has tagname=tag, backup its attr value
			if (obj.childNodes.item(ix).tagName == tag) {
				if (val) {
					obj.childNodes.item(ix)["old"+attr] = obj.childNodes.item(ix)[attr];
					obj.childNodes.item(ix)[attr] = val;
				} else {
					obj.childNodes.item(ix)[attr] = obj.childNodes.item(ix)["old" + attr];
				}
			}
//			debugstring += obj.childNodes.item(ix).tagName;
			if (val) {
				beetSetDescendantAttributeOfTagName(obj.childNodes.item(ix), tag, attr, val);
			} else {
				beetSetDescendantAttributeOfTagName(obj.childNodes.item(ix), tag, attr);
			}
		}
	}
//	alert(debugstring);
}

function beetUpMenuHere(obj) {
	var foo = obj.obj;
	var nn = foo.id;

	if (foo.menu != null) {
	// the menu is already up.  just clear the timeout
		beetClearTimeout(foo);
		return;
	}
	beetSuppressMenusExcept(beetmenubaritems, foo);

	foo.className = "beetmenubaritemselected";

	if (foo.overimage) 
		 beetSetDescendantAttributeOfTagName(foo, "IMG", "src", foo.overimage);

	var arr = foo.menuitems;
	var menu = new createObj("div");
	menu.obj.className="beetmenu"
	
	menu.style.position = "absolute";
	// the next line is a dtek hack to get beet to display over fadeshow
	// animations...
	menu.style.zIndex = 1000; 
	if (foo.menupos == "right") {
		menu.style.left = (findPosX(foo) + foo.offsetWidth)+"px";
		menu.style.top = findPosY(foo)+"px";
	} else if (foo.menupos == "bottom") { 
		menu.style.left = findPosX(foo)+"px";
		menu.style.top = (findPosY(foo) + foo.offsetHeight)+"px";
	} else {
		// don't know this suggested position, so we will passive-aggressively draw it directly on top of the parent element.
		menu.style.left = findPosX(foo)+"px";
		menu.style.top = findPosY(foo)+"px";
	}	

	for (var ix = 0; ix < arr.length; ix++) {
		var item = document.createElement("div");

// experimenting with using tables instead for the internal blocks...
//		item.appendChild(document.createElement("table").insertRow(0).insertCell(0).appendChild(document.createTextNode(arr[ix][0])));
		
		var subspan = document.createElement("span");
		var txt = document.createTextNode(arr[ix][0]);
		subspan.appendChild(txt);
		item.appendChild(subspan);


		item.className = "beetmenuitem";
		if (arr[ix][1] != "") item.title = arr[ix][1];
		item.onmouseover = beetHighlightMenuItem;
		item.onmouseout = beetDeHighlightMenuItem;
		item.userdata = arr[ix][2];
		item.onmouseup = beetClickMenuItem;

		item.parentmenubaritem = foo;
		menu.obj.appendChild(item);
	}
	document.body.appendChild(menu.obj);
	foo.menu = menu;
	foo.menu.style.display = "block";

	// limit the menu to no more than, uh, 30 pixels wider than the widest element.
	// this appears to be necessary because IE5Mac stupidly draws
        // the "menu" div all the way to the right of the screen.
	// to be more principled, we could try to derive maxpad from the stylesheet somehow.
	beetShrinkDivWidth(menu, 30);

	if (foo.menupos == "right") {
	// as per arh1's request, we will only do this for right-drop menus:
		beetShowDivVert(menu, 15);
	}
	// when the menu comes up, there is no timeout.  the timeout gets set when we mouseout.
	beetClearTimeout(foo);
}


// try to show as much of the div on the screen vertically as possible.
// This means move the div up or down so that as much of it is inside the
// viewport as possible.  This function favors the top of the div over
// the bottom of the div.  if it has to leave some of the div out of the
// viewport, it will put the top of the div right at the top of the
// viewport and leave the foot of the div trailing off the bottom.

// if the div is already fully showing, it won't touch it. note also
// that this div should probably already be absolutely positioned.

// it also pushes the div out by vbuffer pixels from the top or bottom
// border of the viewport, as necessary

function beetShowDivVert(divobj, vbuffer) {
	if (!vbuffer) var vbuffer = 0;

	var vpy = getViewPortVScroll();
	var vph = getViewPortHeight();
	var divy = divobj.obj.offsetTop;
	var divh = divobj.obj.offsetHeight;
	
	var odivy = divy; // the original placement.

//	alert(vpy + ". ." + vph + ". ." + odivy + ". ." + divh);

	if ((divy + divh + vbuffer) > (vpy + vph)) {
		divy = vpy + vph - (divh + vbuffer);
	}
	if ((divy - vbuffer)< vpy) {
		divy = vpy + vbuffer;
	}
	if (divy != odivy) {
		divobj.style.top = divy + "px";
	}
}

// this function accepts a div and forces its width to be no more than
// its widest immediate child (plus some maxpad value).  most browsers
// do this naturally, particularly for aboslute or floated divs.  the
// function was written in response to IE5Mac, which doesn't behave as
// the other browsers do, but draws its boundaries all the way to the
// edge of the screen or the containing div.

// actually, it looks at the width of the first child of each of the
// parent node's children: that is, it looks at the "first grandchildren"
// of the parent node.  this is a hack that i can get away with when i
// construct the div and its children by hand because i can insert the
// "subspan" element (as in beetUpMenuHere).  however, it doesn't work for
// other, more arbitrarily-constructed divs.

function beetShrinkDivWidth(divobj, maxpad) {
//	var printstring = "";
	var maxwid = 0;
	for (var ix = 0; ix < divobj.obj.childNodes.length; ix++ ) {
		if (divobj.obj.childNodes.item(ix).firstChild) {
			var wid = divobj.obj.childNodes.item(ix).firstChild.offsetWidth;
//		printstring += menu.obj.childNodes.item(ix).firstChild.firstChild.nodeValue + " " + wid + 
//			"(" + menu.obj.childNodes.item(ix).style.padding + ")\n";
			if (wid > maxwid) maxwid = wid;
		}
	}
//	printstring += "-------------------------\nmax width: "+ maxwid;
//	printstring += "\nfull width: "+ divobj.obj.offsetWidth;
//	printstring += "\npadding: "+ divobj.style.padding;
//	alert (printstring);
	
	if (divobj.obj.offsetWidth > maxwid + maxpad) {
		divobj.style.width = (maxwid + maxpad)+"px";
	}
	//

}

function beetDownMenuHere(obj) {
	var foo = obj.obj;
	foo.className = "beetmenubaritem";
	if (foo.menu != null) {
		foo.menu.style.display = "none";
		document.body.removeChild(foo.menu.obj);
		foo.menu = null;
		if (foo.overimage) 
			 beetSetDescendantAttributeOfTagName(foo, "IMG", "src");

	}
	beetClearTimeout(foo);
}

function beetClearTimeout(mm) {
	if (mm.timeout != null) {
		clearTimeout(mm.timeout);
		mm.timeout = null;
	}
}


function beetResetTimeout(mm) {
	beetClearTimeout(mm);
	if (mm.beettimeoutduration > 0) {
		var funstring = "beetDownMenuByName('"+mm.id+"');";
		mm.timeout = setTimeout(funstring, mm.beettimeoutduration);
	}
}

function beetHighlightMenuItemHere(el) {
//	beetPrintItemToStatus(el);
	el.className = "beetmenuitemhighlight";
	beetClearTimeout(el.parentmenubaritem);
}
function beetDeHighlightMenuItemHere(el) {
	el.className = "beetmenuitem";
	beetResetTimeout(el.parentmenubaritem);
}

function beetClickMenuItemHere(el) {
//	alert(el.userdata);
	location = el.userdata;
}
function beetPrintItemToStatus(el) {
	window.status = "."+el.nodeName + ". ." + el.nodeValue + ". ." + el.userdata+".";
}

//window.onload = beetInitialize;

