// Are we using Navigator/Phoenix/Mozilla?
var gecko   = (navigator.userAgent.toLowerCase().indexOf('gecko')!=-1);
var error   = !(document.createElement && document.getElementById && document.getElementsByTagName);
var geckoV  = (gecko)?navigator.userAgent.replace(/.*Gecko\/([0-9]+).*/gi,'$1'):-1;
var safari	= (navigator.userAgent.toLowerCase().indexOf('safari')!=-1);

var ns62    = {x:0,y:0};
var modern  = {x:0,y:21};
var safari	= {x:0,y:21};

// This reusable object is similar to a thread in that it can have a
// function() or string to be evaluated set on it by calling it's
// setFunction() method. Once the callbacks have been initialized, calling
// start() or stop() will execute the code within. There is also a
// timeout property used to determine how many milliseconds are required
// to wait before the user defined code is executed.
function Thread()
{
    Thread.obj[(this.id=Thread.obj.length)]=this;

    this.running   = false;
    this.instance  = 0;
	this.act       = '';
	this.rfn       = null;
	this.fn        = null;
	this.timeout   = 250;
	
    this.setFunction=function(fn)
	{
		if(typeof fn=='string')
		{
			this.act=fn;
			this.rfn=function()
                {eval(Thread.obj[this.id].act);}
		}
		else
		{
            // This is required as an eval since when the timer calls
            // the function it no longer appears to be part of this
            // object. Calling it absolutely seems to help.
			this.fn=fn;
			eval('this.rfn=function(){var o=Thread.obj['+this.id+'];'+
			'if(!o.running){o.running=true;o.fn(o);o.running=false;}}');
		}
	};
	
	this.start=function()
        {this.instance = setTimeout('Thread.obj['+this.id+'].rfn()',this.timeout)};
	
    this.stop=function()
    	{clearTimeout(this.instance);this.instance = 0;};
}
Thread.obj = new Array();

// Obtains the page coordinates of an object and returns them
// as an object with a x and a y property.
function getPageXY(elm)
    {var point = { x: 0, y: 0 };while (elm){point.x = point.x + parseInt(0+elm.offsetLeft,10);
     point.y = point.y + parseInt(0+elm.offsetTop,10);elm = elm.offsetParent;}return point;}

// Modifies and returns a supplied point object, altering x and y
// by the values supplied.
function offsetXY(point,x,y)
    {if(x&x.y&x.x){y=x.y;x=x.x}point.x=point.x+x;point.y=point.y+y;return point;}

// Modifies the supplied point by offsets that differ by browser type
// and version
function browserOffset(point)
    {if(gecko && geckoV < 20020000) point = offsetXY(point,ns62.x,ns62.y);
     else if((gecko && geckoV > 20020000) || !gecko) point =
     offsetXY(point,modern.x,modern.y); else if(safari) point = offsetXY
     (point,safari.x,safari.y);return point;}

// Sets a given DOM element's physical location. x can optionally be a
// a point object as returned by getPageXY().
function setPageXY(elm,x,y)
    {var parentXY = {x: 0, y: 0 };if(x && x.x && x.y){y = x.y; x = x.x;}
     if (elm.offsetParent){parentXY = getPageXY(elm.offsetParent);}
     elm.style.left='';elm.style.top='';elm.style.left=(x-parentXY.x)+'px';
     elm.style.top=(y-parentXY.y)+'px';}
     
// Makes the supplied element as large as the second element
function minSize(makeThis,atLeastAsBigAsThis)
{
    if(parseInt(0+arguments[1].offsetWidth,10)>parseInt(0+arguments[0].offsetWidth,10))
        arguments[0].style.width = parseInt(0+arguments[1].offsetWidth,10);
}

// Create global menus collection. This collection will contain the DIVs
// that contain the actual submenu content.
window.menus = new Array();
menus.append = function(){var id = this.length; this[id]=arguments[0]; return id;}

// Modifies all <a> tags that possess the makeMenu attribute. The code below
// is applied to each one via the TagProcessor object.
tagProcessor.register('a','makeMenu',function(tag,attr)
{
    if(!attr) // Mac IE (grrr. do everything ourselves)
    {
    	var code = new String(tag.outerHTML);
    	attr = {value:code.replace(/<.*makeMenu="(.+)".*>/gi,'$1')};
    }

    var sDoc        = eval(attr.value);
    var link        = tag.nextSibling;
    var tdNode      = tag.parentNode;
    var div         = document.createElement('div');

    if(!sDoc) {alert('Failed to locate the makeMenu content specified');return;}

    div.id          = 'submenu'+window.menus.append(div);
    div.className   = 'submenu';
    div.anchor      = tag;
    div.point       = browserOffset(getPageXY(tag));
    sDoc.insertNodes(div);

    div.tmShow = new Thread();
    div.tmShow.setFunction(function(){setPageXY(this.submenu,this.submenu.point);this.submenu.style.display='block';minSize(this.submenu,this.tdNode);});
    div.tmShow.submenu=div;
    div.tmShow.tdNode=tdNode;
    div.tmShow.timeout=100;

    div.tmHide = new Thread();
    div.tmHide.setFunction(function(){this.submenu.style.display='none';});
    div.tmHide.submenu=div;
    div.tmHide.timeout=100;

    link.submenu                = div;
    link.onmouseover            = function(){this.submenu.tmShow.start();}
    link.onmouseout             = function(){this.submenu.tmHide.start();}
    link.submenu.onmouseover    = function(){this.tmHide.stop();}
    link.submenu.onmouseout     = function(){this.tmHide.start();}

    eval('addEvent(window,"resize",function(){with(Thread.obj['+ div.tmShow.id +
        '].submenu) {point=browserOffset(getPageXY(anchor));}});');

    document.body.appendChild(link.submenu);
    window.menus[div.id] = div;
});

// Used to return a DOM DocumentFragment that can contain HTML elements.
// These elements can be placed within the submenu's on the page. NOTE:
// Originally this used a DocumentFragment but IE/Mac doens't support it
// so I simulate it using an object as a container. Apparently this includes
// IE/5 Windows.
function CreateContent()
{
    var content = {childNodes:new Array(),
		
	addItem:function(href,text)
    {
        var item = document.createElement('a');
        var body = (text&&text.nodeName)?body:document.createTextNode(text);
		
        item.setAttribute('href',href);
        item.className='item';
        item.appendChild(body);
        
        this.append(item);

        for(var i = 0; i < this.childNodes.length; i++)
       		this.childNodes[i].className='item';
       		
        this.childNodes[this.childNodes.length-1].className='lastitem';
    },
	
	insertNodes:function(elem)
    {
    	for(var i=0, len=this.childNodes.length;i<len;i++)
    		elem.appendChild(this.childNodes[i]);
    	return elem;
    },
	
	append:function(elem)
	{
		this.childNodes[this.childNodes.length]=elem;
	}};
    
    return content;
}

