/*--------------------------------------------------------------------------------\
|                                Popup Menu Class                                 |
|---------------------------------------------------------------------------------|
| Author:       Dan Sutton                                                        |
| Date:         October 3rd, 2008                                                 |
| Version:      2.0.1                                                             |
| Contact:      dan@opwernby.com                                                  |
|---------------------------------------------------------------------------------|
| Dependencies: popups.css, popupSep.png                                   				|
\--------------------------------------------------------------------------------*/

// Control Variable for Popup Menus
var popupControl=
{
	nextEntity:	1,														// Unique entity number for each object created
	items:			[],														// The popup menus defined in the control
	sepImage:		"/images/popup/popupSep.png",	// The one-pixel image used for the Menu separator
	overBack:		"#777777",										// Background colour of highlighted item
	overFore:		"#FFFFFF",										// Foreground colour of highlighted item
	outBack:		"#F7F7F7",										// Background colour of unhighlighted item
	outFore:		"#000000",										// Foreground colour of unhighlighted item
	registry:		[],														// Registry for Items

	// Browser Determination
	browser:	navigator.userAgent.toLowerCase(),
	firefox:	(navigator.userAgent.toLowerCase().indexOf("firefox")>=0),
	ie:				(navigator.userAgent.toLowerCase().indexOf("msie")>=0),
	ie6:			(navigator.userAgent.toLowerCase().indexOf("msie 6")>=0),
	opera:		(navigator.userAgent.toLowerCase().indexOf("opera")>=0),
	netscape:	(navigator.userAgent.toLowerCase().indexOf("netscape")>=0),
	safari:		(navigator.userAgent.toLowerCase().indexOf("safari")>=0),

	// This property is included for when processing has to take place between the point at
	// which the user clicks on the node, and the point at which the menu shows itself (for
	// example, the skipFor() function for the control may require some other events to take
	// place before it can be allowed to happen. The idea is to set it to true in the initial
	// onClick or onMouseUp event for the control, (popupControl.wait=true;) and then set it 
	// back to false when the required processing is complete (see popupControl.show). The 
	// popup menu will not generate itself and display until this value says false.
	wait: false, 
	
	// Adds a new Popup menu
	add: function()
	{
		var x=new popupMenu();
		this.items[this.items.length]=x;
		return x;
	},
	
	register: function(obj)
	{
		this.registry[obj.entity]=obj;
	},

	// Finds the menu element with the given Entity number
	findEntity: function(entity)
	{
		return this.registry[entity];
	},
											
	// Finds the menu bound to the given Control (by name)
	findControl: function(elementName)
	{
		var r=null;
		for (var i=0; (i<this.items.length) && (r==null); i++)
			if (this.items[i].boundTo(elementName))
				r=this.items[i];
		return r;
	},

	// Shows the given menu. Parameters x, y and ct are passed back by the "wait" functionality;
	// the programmer should only pass "entity", which is the menu's entity number.
	show: function(entity,x,y,ct)
	{
		if (!x)
		{
			x=event.x;
			y=event.y;
			ct=event.button;
		}
		if (ct!=1)
		{
			if (this.wait)
			{
				setTimeout("popupControl.show("+entity+","+x+","+y+","+ct+")",50);
			}
			else
			{
				var m=this.findEntity(entity);
				if (m)
					m.show((x+document.body.scrollLeft)-5, (y+document.body.scrollTop)-5);
			}
		}
	},

	// This is the "Show" event for FireFox
	showFireFox: function(e)
	{
		if (e)
		{
			var x=e.clientX;
			var y=e.clientY;
			var ct=(e.button==0? 1 : 0);
			var t=popupControl.findControl(e.currentTarget.id);
			if (t)
				popupControl.show(t.entity,x,y,ct);
		}
		return false;
	},
	
	// This event happens when the menu's <input> control loses focus
	blur: function(entity)
	{
		var m=this.findEntity(entity);
		if (m)
			if (m.inDiv)
			{
				var c=document.getElementById(m.text);
				c.focus();
			}
			else
				m.hide();
	},

	// This hides the given menu
	hide: function(entity)
	{
		var m=this.findEntity(entity);
		if (m)
		{
			m.inDiv=false;
			m.hide();
		}
	},

	// This happens when the mouse passes over a menu selection
	over: function(control)
	{
		control.style.backgroundColor=this.overBack;
		control.style.color=this.overFore;
	},
		
	// This happens when the mouse leaves a menu selection
	out: function(control)
	{
		control.style.backgroundColor=this.outBack;
		control.style.color=this.outFore;
	},

	// This happens when the user clicks on a menu selection
	click: function(entity)
	{
		var x=this.findEntity(entity);
		if (x)
		{
			this.hide(x.owner.entity);
			if (x.func)
				x.func();
		}
	},

	// This happens when the user's mouse is moving over a Menu
	overMenu: function(entity)
	{
		var x=this.findEntity(entity);
		if (x)
			x.inDiv=true;
	},

	// This happens when the user's mouse moves outside a Menu
	outMenu: function(entity)
	{
		var x=this.findEntity(entity);
		if (x)
			x.inDiv=false;
	},

	// This returns the inner width of the current Window
	innerWidth: function()
	{
		if (window.innerWidth)
			return window.innerWidth;
		else if (document.body)
			return document.body.clientWidth;
		else if (document.documentElement && document.documentElement.clientWidth)
			return document.documentElement.clientWidth;
		else
			return screen.width;
	},

	// This returns the inner height of the current Window
	innerHeight: function()
	{
		if (window.innerWidth)
			return window.innerHeight;
		else if (document.body)
			return document.body.clientHeight;
		else if (document.documentElement && document.documentElement.clientWidth)
			return document.documentElement.clientHeight;
		else
			return screen.height;
	}
};

// Superclass for Popup Menu objects
function popupClass(){}

// States whether the menu object exists on the screen (i.e. has been drawn)
popupClass.prototype.exists=function()
{
	var r=false;
	var x=document.getElementById(this.control);
	if (x)
		r=true;
	return r;
};

// Class for Popup Menu (subclassed from popupClass)
function popupMenu()
{
	this.entity=popupControl.nextEntity++;	// The object's entity number
	this.control="popup_"+this.entity;			// The name of the primary HTML element in the menu
	this.isMenu=true;												// States that this is a menu
	this.items=[];													// Items which exist under this element
	this.inDiv=false;												// States whether the mouse is over the menu
	this.shadow=this.control+"_sh";					// Name of Shadow element
	this.text=this.control+"_txt";					// Name of <input> control
	this.table=this.control+"_tb";					// Name of main <table> element
	this.contents=this.control+"_cnt";			// Name of <TD> element containing Options
	this.elemIDs=[];												// ID's of controls bound to this menu
	popupControl.register(this);
}
popupMenu.prototype=new popupClass;

// Adds an option to the Menu. Parameters: Visible Text, Function to execute on onClick;
// function which, if it returns true, causes the element not to be displayed. (only the
// first parameter is required).
popupMenu.prototype.add=function(text, func, skipFor)
{
	var x=new popupItem(this, text, func, skipFor);
	this.items[this.items.length]=x;
	if (this.exists())
		this.fillIn();
	return x;
};

// Adds a menu separator (the skipFor parameter works in the same way as in the
// add() method, and is optional.
popupMenu.prototype.addSep=function(skipFor)
{
	var x=new popupSep(this, skipFor);
	this.items[this.items.length]=x;
	if (this.exists())
		this.fillIn();
};

// Finds the given option (by Entity)
popupMenu.prototype.findEntity=function(entity)
{
	return popupControl.findEntity(entity);
};

popupMenu.prototype.boundTo=function(elementName)
{
	var r=false;
	for (var i=0; i<this.elemIDs.length && !r; i++)
		r=(this.elemIDs[i]==elementName);
	return r;
};

// Binds the menu to the given HTML element
popupMenu.prototype.bind=function(elementName)
{
	var x=document.getElementById(elementName);
	if (x)
	{
		if (popupControl.ie)
			x.oncontextmenu=new Function("{popupControl.show("+this.entity+");return false;}");
		else
			x.oncontextmenu=popupControl.showFireFox;
		this.elemIDs[this.elemIDs.length]=elementName;
	}
};

// Returns the HTML representation of the menu
popupMenu.prototype.toString=function()
{
	var HTML="<div class='popupShadowDiv"+(popupControl.ie6? "_msie":"")+"'"
	 +" id='"+this.shadow+"'>&nbsp;</div>"
	 +"<div class='popupDiv' id='"+this.control+"'"
	 +" onMouseOver='javascript:popupControl.overMenu("+this.entity+");'"
	 +" onMouseOut='javascript:popupControl.outMenu("+this.entity+");'>"
	 +"<table cellspacing=0 cellpadding=0 border=0><td class='popupLeft'>"
	 +"<input type='text' readonly id='"+this.text
	 +"' name='"+this.text+"' class='popupField'"
	 +" onblur='javascript:popupControl.blur("+this.entity+");'></td><td>"
	 +"<table cellpadding=0 cellspacing=0 border=0 id='"+this.table+"'>"
	 +"<tr class='popupHeaderTR' valign='top'>"
	 +"<td class='popupHeader' align='center' nowrap>|||||||||||||||||</td>"
	 +"</tr><tr valign='top'><td id='"+this.contents+"'>"
	 +this.lines()
	 +"</td></tr></table></td></table></div>"; 
	return HTML;
};

// Returns the HTML representation of the menu Options
popupMenu.prototype.lines=function()
{
	var HTML="<table class='popupDetailTR' cellpadding=1 cellspacing=0 border=0>";
	for (i in this.items)
		HTML+=this.items[i].toString();
	HTML+="</table>"; 
	return HTML;
};

// Shows the menu. This will do nothing if the menu has no visible options.
popupMenu.prototype.show=function(x,y)
{
	var d=document.getElementById(this.control);
	d.style.left=x;
	d.style.top=y;
	if (this.fillIn())
	{
		var h=parseFloat(d.offsetHeight);
		var w=parseFloat(d.offsetWidth);
		var sh=parseFloat(popupControl.innerHeight()-2);
		var sw=parseFloat(popupControl.innerWidth()-2);
		if (y+h>sh)
			d.style.top=sh-h;
		if (x+w>sw)
			d.style.left=sw-w;
		d.style.visibility="visible";
		this.showShadow(d);
		d=document.getElementById(this.text);
		d.focus();
	}
};

// Hides the menu
popupMenu.prototype.hide=function()
{
	var d=document.getElementById(this.control);
	d.style.visibility="hidden";
	d.style.top=0;
	d.style.left=0;
	d=document.getElementById(this.shadow);
	d.style.visibility="hidden";
	d.style.top=0;
	d.style.left=0;
};

// Fills in the Menu Options. Returns true or false depending upon whether
// it finds any options to display.
popupMenu.prototype.fillIn=function()
{
	var r=false;
	var HTML=this.lines();
	if (HTML.indexOf("<tr")>=0)
	{
		r=true;
		var d=document.getElementById(this.control);
		var t=document.getElementById(this.table);
		var c=document.getElementById(this.contents);
		c.innerHTML=HTML;
		if (t.offsetHeight<150)
			d.style.height=t.offsetHeight+3;
	}
	return r;
};

popupMenu.prototype.showShadow=function(menuElement)
{
	var s=document.getElementById(this.shadow);
	s.style.top=menuElement.offsetTop+2;
	s.style.left=menuElement.offsetLeft+2;
	s.style.width=menuElement.offsetWidth;
	s.style.height=menuElement.offsetHeight;
	s.style.visibility="visible";
};

// Menu Option superclass. "skipFor" is a function which, when it returns false, causes
// the popupLine not to be displayed.
function popupLineClass(){}
popupLineClass.prototype=new popupClass;
popupLineClass.prototype.isMenu=false;
	
// Returns the HTML representation of the Menu Option
popupLineClass.prototype.toString=function()
{
	var ok=true;
	var r="";
	if (this.skipFor)
	try
	{
		ok=!(this.skipFor());
	}
	catch(e){}
	if (ok)
		r="<tr id='"+this.control+"'>"+this.innerString()+"</tr>";
	return r;
};

// Menu Option superclass. "skipFor" is a function which, when it returns false, causes
// the popupLine not to be displayed.
function popupLine(owner,skipFor)
{
	this.entity=popupControl.nextEntity++;	// The object's entity number
	this.control="popup_"+this.entity;			// The name of the primary HTML element in the menu
	this.owner=owner;												// States which menu owns this item
	this.skipFor=skipFor;										// sets the "skipFor" function (optional)
	this.items=[];													// Items which exist under this element
	popupControl.register(this);
}
popupLine.prototype=new popupLineClass;

// This is a default innerString which is never used, but is included for
// the sake of consistency.
popupLineClass.prototype.innerString=function()
{
	return "&nbsp;";
};

// Popup Menu Separator, subclassed from popupLine.
function popupSep(owner, skipFor)
{
	this.entity=popupControl.nextEntity++;	// The object's entity number
	this.control="popup_"+this.entity;			// The name of the primary HTML element in the menu
	this.owner=owner;												// States which menu owns this item
	this.skipFor=skipFor;										// sets the "skipFor" function (optional)
	this.owner=owner;												// States which menu owns this item
	this.skipFor=skipFor;										// sets the "skipFor" function (optional)
	this.items=[];													// Items which exist under this element
	popupControl.register(this);
}
popupSep.prototype=new popupLine;

// Returns the innerHTML representation of the Separator
popupSep.prototype.innerString=function()
{
	var HTML="<td><img src='"+popupControl.sepImage+"' width='100%' height=1></td>";
	return HTML;
}

// Popup Menu Item, subclassed from popupLine. "text" is the text to display; "func"
// is the function to execute when the item is selected. The last two parameters are optional.
function popupItem(owner, text, func, skipFor)
{
	this.entity=popupControl.nextEntity++;	// The object's entity number
	this.control="popup_"+this.entity;			// The name of the primary HTML element in the menu
	this.owner=owner;												// States which menu owns this item
	this.skipFor=skipFor;										// sets the "skipFor" function (optional)
	this.display=text;											// Display Text
	this.func=func;													// Function to execute when the option is selected
	this.owner=owner;												// States which menu owns this item
	this.skipFor=skipFor;										// sets the "skipFor" function (optional)
	this.items=[];													// Items which exist under this element
	popupControl.register(this);
}
popupItem.prototype=new popupLine;

// innerHTML representation of the Menu Item
popupItem.prototype.innerString=function()
{
	var HTML="<td class='popupOption' nowrap"
	 +" onMouseOver='javascript:popupControl.over(this);'"
	 +" onMouseOut='javascript:popupControl.out(this);'"
	 +" onClick='javascript:popupControl.click("+this.entity+");'>"
	 +"&nbsp;"+this.display+"&nbsp;</td>";
	return HTML;
};