// To create a new popup menu:
// 	<element id="x">Click me <script type="text/javascript"> vBmenu.register('x'); </script></element>
//
// To create a dynamic popup menu with a title and two options:
// 	<element id="x">Click me</element>
//	<script type="text/javascript">
// 	menu = new vB_Menu_Builder('x');
// 	menu.set_title('My Menu');
// 	menu.add_option('Option 1', 'script.php?opt=1');
// 	menu.add_option('Option 2', 'script.php?opt=2');
// 	menu.build();
//	</ script>

// #############################################################################
// vB_Popup_Handler CLASS
// #############################################################################

function vB_Popup_Handler() {
	this.open_steps = 0;
	this.open_fade = false;
	this.active = false;
	this.menus = new Array();
	this.activemenu = null;
	this.hidden_selects = new Array();
}

// *****************************************************************************
// vB_Popup_Handler methods
// *****************************************************************************

// Activate / Deactivate the menu system
// @param	boolean	Active state for menus
vB_Popup_Handler.prototype.activate = function(active) {
	this.active = active;
}

// Register a control object as a menu control
// @param	string	ID of the control object
// @param	boolean	Do not add an image (true)
// @return	vB_Popup_Menu
vB_Popup_Handler.prototype.register = function(controlkey, noimage) {
	this.menus[controlkey] = new vB_Popup_Menu(controlkey, noimage);
	return this.menus[controlkey];
}
// Hide active menu
vB_Popup_Handler.prototype.hide = function() {
	if (this.activemenu != null) {
		this.menus[this.activemenu].hide();
	}
}

// *****************************************************************************
// initialize menu registry
// *****************************************************************************

vBmenu = new vB_Popup_Handler();

// Function to allow anything to hide all menus
// @param	event	Event object
// @return	mixed
function vbmenu_hide(e) {
	if (e && e.button && e.button != 1 && e.type == 'click') {
		return true;
	} else {
		vBmenu.hide();
	}
}

// #############################################################################
// CLASS: vB_Popup_Menu
// #############################################################################

// Menu class constructor
// Manages a single menu and control object
// Initializes control object
// @param	string	ID of the control object
function vB_Popup_Menu(controlkey, noimage) {
	this.yOffset = 1;
	this.xOffset = 0;
	this.controlkey = controlkey;
	this.menuname = this.controlkey.split('.')[0] + '_menu';
	this.init_control(noimage);
	if (elementById(this.menuname))	{
		this.init_menu();
	}
	this.slide_open = (is_opera ? false : true);
	this.open_steps = vBmenu.open_steps;
}

// *****************************************************************************
// vB_Popup_Menu methods
// *****************************************************************************

// Initialize the control object
vB_Popup_Menu.prototype.init_control = function(noimage) {
	this.controlobj = elementById(this.controlkey);
	this.controlobj.state = false;

	if (this.controlobj.firstChild && (this.controlobj.firstChild.tagName == 'TEXTAREA' || this.controlobj.firstChild.tagName == 'INPUT')) {
		alert('cannot add menu on textarea or input tags !\r\nPlease check your code');
	} else {
		if (!noimage && !(is_mac && is_ie)) {
			var space = document.createTextNode(' ');
			this.controlobj.appendChild(space);
			var img = document.createElement('img');
			img.src = IMGDIR_MISC + '/menu_open.gif';
			img.border = 0;
			img.title = '';
			img.alt = '';
			this.controlobj.appendChild(img);
		}

		this.controlobj.unselectable = true;
		if (!noimage) {
			this.controlobj.style.cursor = pointer_cursor;
		}
		this.controlobj.onclick = vB_Popup_Events.prototype.controlobj_onclick;
		this.controlobj.onmouseover = vB_Popup_Events.prototype.controlobj_onmouseover;
	}
}

// Init the popup menu object
vB_Popup_Menu.prototype.init_menu = function() {
	this.menuobj = elementById(this.menuname);

	if (this.menuobj && !this.menuobj.initialized) {
		this.menuobj.initialized = true;
		this.menuobj.onclick = e_by_gum;
		this.menuobj.style.position = 'absolute';
		this.menuobj.style.zIndex = 50;
		// init popup filters (ie only)
		if (is_ie && !is_mac) {
			this.menuobj.style.filter += "progid:DXImageTransform.Microsoft.alpha(enabled=0,opacity=100)";
		}
		this.init_menu_contents();
	}
}

// Init the popup menu contents
vB_Popup_Menu.prototype.init_menu_contents = function() {
	var tds = elementsByTagName(this.menuobj, 'td');
	for (var i = 0; i < tds.length; i++) {
		if (tds[i].className == 'vbmenu_option') {
			if (tds[i].title && tds[i].title == 'nohilite') {
				// not an active cell
				tds[i].title = '';
			} else {
				// create a reference back to the menu class
				tds[i].controlkey = this.controlkey;

				// handle mouseover / mouseout highlighting events
				tds[i].onmouseover = vB_Popup_Events.prototype.menuoption_onmouseover;
				tds[i].onmouseout = vB_Popup_Events.prototype.menuoption_onmouseout;

				if (typeof tds[i].onclick == 'function') {
					// allow onclick events from <td> elements to override <a> elements inside
					tds[i].ofunc = tds[i].onclick;
					tds[i].onclick = vB_Popup_Events.prototype.menuoption_onclick_function;
				} else {
					// attempt to emulate a click on internal <a> elements
					tds[i].onclick = vB_Popup_Events.prototype.menuoption_onclick_link;
				}

				// wondering what this was supposed to do actually...
				if (!is_saf && !is_kon) {
					try {
						links = elementsByTagName(tds[i], 'a');
						for (var j = 0; j < links.length; j++) {
							if (typeof links[j].onclick  == 'undefined') {
								links[j].onclick = e_by_gum;
							}
						}
					} catch(e) {
						// hmmm...
					}
				}
			}
		}
	}
	
	
	
	// Added by Shawn for Forum Jump
	var tds = elementsByTagName(this.menuobj, 'th');
	for (var i = 0; i < tds.length; i++) {
		if (tds[i].className == 'vbmenu_option') {
			if (tds[i].title && tds[i].title == 'nohilite') {
				// not an active cell
				tds[i].title = '';
			} else {
				// create a reference back to the menu class
				tds[i].controlkey = this.controlkey;

				// handle mouseover / mouseout highlighting events
				tds[i].onmouseover = vB_Popup_Events.prototype.menuoption_onmouseover;
				tds[i].onmouseout = vB_Popup_Events.prototype.menuoption_onmouseout;

				if (typeof tds[i].onclick == 'function') {
					// allow onclick events from <td> elements to override <a> elements inside
					tds[i].ofunc = tds[i].onclick;
					tds[i].onclick = vB_Popup_Events.prototype.menuoption_onclick_function;
				} else {
					// attempt to emulate a click on internal <a> elements
					tds[i].onclick = vB_Popup_Events.prototype.menuoption_onclick_link;
				}

				// wondering what this was supposed to do actually...
				if (!is_saf && !is_kon) {
					try {
						links = elementsByTagName(tds[i], 'a');
						for (var j = 0; j < links.length; j++) {
							if (typeof links[j].onclick  == 'undefined') {
								links[j].onclick = e_by_gum;
							}
						}
					} catch(e) {
						// hmmm...
					}
				}
			}
		}
	}	
}

// Show the menu
// @param	object	The control object calling the menu
// @param	boolean	Use slide (false) or open instantly? (true)
vB_Popup_Menu.prototype.show = function(obj, instant)
{
	if (!vBmenu.active)	{
		return false;
	} else if (!this.menuobj) {
		this.init_menu();
	}

	if (!this.menuobj) {
		return false;
	}

	if (vBmenu.activemenu != null) {
		vBmenu.menus[vBmenu.activemenu].hide();
	}

	vBmenu.activemenu = this.controlkey;

	this.menuobj.style.display = '';
	if (vBmenu.slide_open) {
		this.menuobj.style.clip = 'rect(auto, auto, auto, auto)';
	}
	this.pos = this.fetch_offset(obj);
	this.leftpx = this.pos['left'];
	this.toppx = this.pos['top'] + obj.offsetHeight;

	if ((this.leftpx + this.menuobj.offsetWidth) >= document.body.clientWidth && (this.leftpx + obj.offsetWidth - this.menuobj.offsetWidth) > 0) {
		this.leftpx = this.leftpx + obj.offsetWidth - this.menuobj.offsetWidth;
		this.direction = 'right';
	} else {
		this.direction = 'left'
	}

	this.menuobj.style.left = (this.leftpx + this.xOffset) + 'px';
	this.menuobj.style.top  = (this.toppx + this.yOffset) + 'px';

	if (!instant && this.slide_open) {
		this.intervalX = Math.ceil(this.menuobj.offsetWidth / this.open_steps);
		this.intervalY = Math.ceil(this.menuobj.offsetHeight / this.open_steps);
		this.slide((this.direction == 'left' ? 0 : this.menuobj.offsetWidth), 0, 0);
	} else if (this.menuobj.style.clip && vBmenu.slide_open) {
		this.menuobj.style.clip = 'rect(auto, auto, auto, auto)';
	}

	// deal with IE putting <select> elements on top of everything
	this.handle_overlaps(true);

	if (this.controlobj.editorid) {
		this.controlobj.state = true;
		this.controlobj.editor.menu_context(this.controlobj, 'mousedown');
	}
}

// Hide the menu
vB_Popup_Menu.prototype.hide = function(e) {

	if (e && e.button && e.button != 1) {
		// get around some context menu issues etc.
		return true;
	}

	this.stop_slide();
	this.menuobj.style.display = 'none';
	this.handle_overlaps(false);
	
	if (this.controlobj.editorid) {
		this.controlobj.state = false;
		this.controlobj.editor.menu_context(this.controlobj, 'mouseout');
	}

	vBmenu.activemenu = null;
}

// Hover behaviour for control object
vB_Popup_Menu.prototype.hover = function(obj) {
	if (vBmenu.activemenu != null) {
		if (vBmenu.menus[vBmenu.activemenu].controlkey != this.id) {
			this.show(obj, true);
		}
	}
}

// Simulate clicking an option in a menu
// @param	event	Event object
// @param	object	Clicked object
vB_Popup_Menu.prototype.choose = function(e, obj) {
	var links = elementsByTagName(obj, 'a');
	if (links[0]) {
		if (is_ie) {
			// use this in IE to send HTTP_REFERER
			links[0].click();
			window.event.cancelBubble = true;
		} else {
			// other browsers can use this
			if (e.shiftKey) {
				window.open(links[0].href);
				e.stopPropagation();
				e.preventDefault();
			} else {
				window.location = links[0].href;
				e.stopPropagation();
				e.preventDefault();
			}
		}
		this.hide();
	}
}

// Slides menu open
// @param	integer	Clip X
// @param	integer	Clip Y
// @param	integer	Opacity (0-100)
vB_Popup_Menu.prototype.slide = function(clipX, clipY, opacity) {
	if (this.direction == 'left' && (clipX < this.menuobj.offsetWidth || clipY < this.menuobj.offsetHeight)) {
		if (vBmenu.open_fade) {	
			opacity += 10;
			setOpacity(this.menuobj, opacity)
		}
		clipX += this.intervalX;
		clipY += this.intervalY;
		this.menuobj.style.clip = "rect(auto, " + clipX + "px, " + clipY + "px, auto)";
		this.slidetimer = setTimeout("vBmenu.menus[vBmenu.activemenu].slide(" + clipX + ", " + clipY + ", " + opacity + ");", 0);
	} else if (this.direction == 'right' && (clipX > 0 || clipY < this.menuobj.offsetHeight)) {
		if (vBmenu.open_fade) {	
			opacity += 10;
			setOpacity(this.menuobj, opacity)
		}		
		clipX -= this.intervalX;
		clipY += this.intervalY;
		this.menuobj.style.clip = "rect(auto, " + this.menuobj.offsetWidth + "px, " + clipY + "px, " + clipX + "px)";
		this.slidetimer = setTimeout("vBmenu.menus[vBmenu.activemenu].slide(" + clipX + ", " + clipY + ", " + opacity + ");", 0);
	} else {
		this.stop_slide();
	}
}

// Abort menu slider
vB_Popup_Menu.prototype.stop_slide = function() {
	clearTimeout(this.slidetimer);
	this.menuobj.style.clip = 'rect(auto, auto, auto, auto)';
	if (vBmenu.open_fade) {	
		setOpacity(this.menuobj, 100)	
	}
}

// Fetch offset of an object
// @param	object	The object to be measured
// @return	array	The measured offsets left/top
vB_Popup_Menu.prototype.fetch_offset = function(obj) {
	var left_offset = obj.offsetLeft;
	var top_offset = obj.offsetTop;
	while ((obj = obj.offsetParent) != null) {
		left_offset += obj.offsetLeft;
		top_offset += obj.offsetTop;
	}
	return { 'left' : left_offset, 'top' : top_offset };
}

// Detect an overlap of an object and a menu
// @param	object	Object to be tested for overlap
// @param	array	Array of dimensions for menu object
// @return	boolean	True if overlap
vB_Popup_Menu.prototype.overlaps = function(obj, m) {
	var s = new Array();
	var pos = this.fetch_offset(obj);
	s['L'] = pos['left'];
	s['T'] = pos['top'];
	s['R'] = s['L'] + obj.offsetWidth;
	s['B'] = s['T'] + obj.offsetHeight;
	if (s['L'] > m['R'] || s['R'] < m['L'] || s['T'] > m['B'] || s['B'] < m['T']) {
		return false;
	}
	return true;
}

// Handle IE overlapping <select> elements
// @param	boolean	Hide (true) or show (false) overlapping <select> elements
vB_Popup_Menu.prototype.handle_overlaps = function(dohide) {
	if (is_ie) {
		var selects = elementsByTagName(document, 'select');

		if (dohide) {
			var menuarea = new Array(); menuarea = {
				'L' : this.leftpx,
				'R' : this.leftpx + this.menuobj.offsetWidth,
				'T' : this.toppx,
				'B' : this.toppx + this.menuobj.offsetHeight
			};
			for (var i = 0; i < selects.length; i++) {
				if (this.overlaps(selects[i], menuarea)) {
					var hide = true;
					var s = selects[i];
					while (s = s.parentNode) {
						if (s.className == 'vbmenu_popup') {
							hide = false;
							break;
						}
					}
					if (hide) {
						selects[i].style.visibility = 'hidden';
						array_push(vBmenu.hidden_selects, i);
					}
				}
			}
		} else {
			while (true) {
				var i = array_pop(vBmenu.hidden_selects);
				if (typeof i == 'undefined' || i == null) {
					break;
				} else {
					selects[i].style.visibility = 'visible';
				}
			}
		}
	}
}
// End Menu class
// ####################################################################################


// ####################################################################################
// CLASS: Menu event handler
// ####################################################################################

// Class containing menu popup event handlers
function vB_Popup_Events()
{
}

// Handles control object click events
vB_Popup_Events.prototype.controlobj_onclick = function(e)
{
	do_an_e(e);
	if (vBmenu.activemenu == null || vBmenu.menus[vBmenu.activemenu].controlkey != this.id)	{
		vBmenu.menus[this.id].show(this);
	} else {
		vBmenu.menus[this.id].hide();
	}
};

// Handles control object mouseover events
vB_Popup_Events.prototype.controlobj_onmouseover = function(e) {
	do_an_e(e);
	vBmenu.menus[this.id].hover(this);
};

// Handles menu option click events for options with onclick events
vB_Popup_Events.prototype.menuoption_onclick_function = function(e) {
	this.ofunc(e);
	vBmenu.menus[this.controlkey].hide();
};

// Handles menu option click events for options containing links
vB_Popup_Events.prototype.menuoption_onclick_link = function(e) {
	vBmenu.menus[this.controlkey].choose(e, this);
};

// Handles menu option mouseover events
vB_Popup_Events.prototype.menuoption_onmouseover = function(e) {
	this.className = 'vbmenu_hilite';
};


// Handles menu option mouseout events
vB_Popup_Events.prototype.menuoption_onmouseout = function(e) {
	this.className = 'vbmenu_option';
};

// End Menu event handler class
// ####################################################################################

// ####################################################################################
// Browser detection and limitation workarounds
// ####################################################################################

	var userAgent = navigator.userAgent.toLowerCase();
	var is_opera  = (userAgent.indexOf('opera') != -1);
	var is_saf    = ((userAgent.indexOf('applewebkit') != -1) || (navigator.vendor == 'Apple Computer, Inc.'));
	var is_webtv  = (userAgent.indexOf('webtv') != -1);
	var is_ie     = ((userAgent.indexOf('msie') != -1) && (!is_opera) && (!is_saf) && (!is_webtv));
	var is_ie4    = ((is_ie) && (userAgent.indexOf('msie 4.') != -1));
	var is_moz    = ((navigator.product == 'Gecko') && (!is_saf));
	var is_kon    = (userAgent.indexOf('konqueror') != -1);
	var is_ns     = ((userAgent.indexOf('compatible') == -1) && (userAgent.indexOf('mozilla') != -1) && (!is_opera) && (!is_webtv) && (!is_saf));
	var is_ns4    = ((is_ns) && (parseInt(navigator.appVersion) == 4));
	var is_mac    = (userAgent.indexOf('mac') != -1);

// ####################################################################################
// Browser limitation workarounds
// ####################################################################################

	var pointer_cursor = (is_ie ? 'hand' : 'pointer');

// ####################################################################################
// Cross Browser - Objects and Elements Function
// ####################################################################################


	// Emulate opacity in many browser
	// @param	element	eg:document.elementById('id2')
	// @param	integer	opacity (eg:95)	
	function setOpacity(el, opacity)
	{
		if(el)
		{
			if (!opacity){var op=100;} else {var op=opacity}
			el.style.filter = 'alpha(opacity:' + op + ')'; /* MSIE */
			el.style.KHTMLOpacity = op/100; /* Konqueror & Safari */
			el.style.MozOpacity = op/100; /* Gecko browsers */
			el.style.opacity = op/100; /* CSS3 opacity */
		}
	}
	
	// Emulate document.getElementsByTagName
	// @param	object	Parent tag (eg: document)
	// @param	string	Tag type (eg: 'td')
	// @return	array
	function elementsByTagName(parentobj, tag)
	{
		if (typeof parentobj.getElementsByTagName != 'undefined')
		{
			return parentobj.getElementsByTagName(tag);
		}
		else if (parentobj.all && parentobj.all.tags)
		{
			return parentobj.all.tags(tag);
		}
		else
		{
			return null;
		}
	}
	
	// Emulate document.getElementById
	// @param	string	Object ID
	// @return	mixed	null if not found, object if found
	function elementById(oid) {
		if (document.getElementById){
			return document.getElementById(oid);
		} else if (document.all) {
			return document.all[oid];
		} else if (document.layers) {
			return document.layers[oid];
		} else {
			return null;
		}
	}
// ####################################################################################
// Cross Browser - Array Function
// ####################################################################################

	// Push function for browsers that don't have it built in
	// @param	array	Array onto which to push
	// @param	mixed	Value to push onto array
	// @return	integer	Length of array
	
	function array_push(a, value) {
		a[a.length] = value;
		return a.length;
	}

	// Pop function for browsers that don't have it built in
	// @param	array	Array from which to pop
	// @return	mixed	null on empty, value on success
	function array_pop(a) {
		if (typeof a != 'object' || !a.length){
			return null;
		} else {
			var r = a[a.length - 1];
			a.length--;
			return r;
		}
	}

// ####################################################################################
// Cross Browser - Event handlers
// ####################################################################################


	// Cross browser events handles and prevents bubbling
	// @param	Event Object
	// @return	Event Object
	function do_an_e(eo) {
		if (!eo || is_ie) {
			window.event.returnValue = false;
			window.event.cancelBubble = true;
			return window.event;
		} else {
			eo.stopPropagation();
			eo.preventDefault();
			return eo;
		}
	}
	
	// Cross browser events handles and prevents bubbling in a lesser way than do_an_e()
	// @param	Event Object
	// @return	Event Object
	function e_by_gum(eo) {
		if (!eo || is_ie) {
			window.event.cancelBubble = true;
			return window.event;
		} else {
			if (eo.target.type == 'submit'){
				// naughty safari
				eo.target.form.submit();
			}
			eo.stopPropagation();
			return eo;
		}
	}