// Copyright (C) 2005 Ilya S. Lyubinskiy. All rights reserved.
// Technical support: http://www.php-development.ru/
//
// YOU MAY NOT
// (1) Remove or modify this copyright notice.
// (2) Distribute this code, any part or any modified version of it.
//     Instead, you can link to the homepage of this code:
//     http://www.php-development.ru/javascripts/dropdown.php.
//
// YOU MAY
// (1) Use this code on your website.
// (2) Use this code as a part of another product provided that
//     its main use is not creating javascript menus.
//
// NO WARRANTY
// This code is provided "as is" without warranty of any kind, either
// expressed or implied, including, but not limited to, the implied warranties
// of merchantability and fitness for a particular purpose. You expressly
// acknowledge and agree that use of this code is at your own risk.


// NOTE this has been heavily modified from the original code.
// ----- Popup Control ---------------------------------------------------------


// This array keeps a list of objects that are still open
var open_menus = new Array();


/**
 * Handle a show request
 */
function at_show()
{
	// The parent for this link
  var p = document.getElementById(this["at_parent"]);
  
	// The child for this link
	var c = document.getElementById(this["at_child"]);

	// Show the child for this link
  at_show_aux(p.id, c.id);

	// remove the time out that will hide the child
  clearTimeout(c["at_timeout"]);
	
	// Close any other children of this parent item
	closeOtherMenuThreads(p);

	// Add the child in this menu link to the array of currently shown menu items
	addToOpenMenuArray(c);
}

/**
 * show the target of a given parent/child set
 * @param {Object} parent
 * @param {Object} child
 */
function at_show_aux(parent, child)
{
  // parent of the link in question
	var p = getElementByIdOrElement(parent);

	// child of the link in question
	var c = getElementByIdOrElement(child);

  // Initialize the top and left values for the child by looking at the parent's width and height
	var top  = (c["at_position"] == "y") ? p.offsetHeight + 2 : -5;
  var left = (c["at_position"] == "x") ? p.offsetWidth + 2 : 0;
	
	// Set the child's position and visibility
  c.style.position   = "absolute";
  c.style.top        = top +'px';
  c.style.left       = left+'px';
  c.style.visibility = "visible";
}

/**
 * Handle a hide request
 * @param {Object} evt
 */
function at_hide( evt )
{
  // The child for this link
	var c = document.getElementById(this["at_child"]);

	// --- Check to see if we're still inside the menu ('mouseout' isnt always 'out')
	// Get the event target
	evt = (evt) ? evt : ((window.event) ? event : null);
	
	// Get the element that the mouseout was 'to'
	testElement = evt.relatedTarget || evt.toElement;
	
	// If any of the parents of this 'to' element is the child in the menu link (in other words, if we're still inside the child element) 
	if( isAncestor(c, testElement) ) {
		// Then don't consider this a mouse out just yet, and return
		return;
	}
		
	// Else, set a timer so that, after a reasonable time, the item will be hidden
	else {
		c["at_timeout"] = setTimeout("at_hide_aux('"+c.id+"');", 150);
	}	
}

/**
 * Do the actual hiding of the target object
 * @param {Object} target_id
 */
function at_hide_aux(target)
{
  // target in question
	var t = getElementByIdOrElement(target);

	// Set the target's visibility to hidden
	t.style.visibility = 'hidden';
	
	// Remove the target from the array of 'open' menu items
	removeFromOpenMenuArray( t );
}


/**
 * Handle a click event
 */
function at_click()
{
	var p = document.getElementById(this["at_parent"]);
	var c = document.getElementById(this["at_child"]);

	if (c.style.visibility != "visible") {
  	at_show_aux(p.id, c.id);
  }
  else {
  	c.style.visibility = "hidden";
  }

  return false;
}

/**
 * create a link between a parent and a child for a menu display
 * @param {Object} parent id of visible html element
 * @param {Object} child id of invisible html element that will be dropdowned
 * @param {Object} showtype "click" = you should click the parent to show/hide the child, "hover" = you should place the mouse over the parent to show the child
 * @param {Object} position "x" = the child is displayed to the right of the parent, "y" = the child is displayed below the parent
 * @param {Object} cursor Omit to use default cursor or check any CSS manual for possible values of this field
 */
function at_attach(parent, child, showtype, position, cursor)
{
  // parent of the link in question
	var p = getElementByIdOrElement(parent);

	// child of the link in question
	var c = getElementByIdOrElement(child);

	// Set the child and parent with identical copies of this link's info
  p["at_parent"]     = p.id;
  c["at_parent"]     = p.id;
  p["at_child"]      = c.id;
  c["at_child"]      = c.id;
  p["at_position"]   = position;
  c["at_position"]   = position;

	// Set default styles for the parent and child
	p.className				 = p.className + ' parent_menu';
	p.style.position   = 'relative';
 	c.style.position   = "absolute";
  c.style.visibility = "hidden";
  if (cursor != undefined) p.style.cursor = cursor;

	// set the event handlers for the parent and child elements
  switch (showtype)
  {
    case "click":
      p.onclick     = at_click;
      p.onmouseout  = at_hide;
      c.onmouseover = at_show;
      c.onmouseout  = at_hide;
      break;
    case "hover":
      p.onmouseover = at_show;
      p.onmouseout  = at_hide;
      c.onmouseover = at_show;
      c.onmouseout  = at_hide;
      break;
  }
}


/**
 * Add a specified object to the array of open menus
 * @param {Object} item
 */
function addToOpenMenuArray(target)
{
	// the item in question
	var item = getElementByIdOrElement(target);
	
	removeFromOpenMenuArray(item);
	open_menus.push(item);
}

/**
 * Remove the specified object from the array of open menus, based on it's id parameter
 * @param {Object} item
 */
function removeFromOpenMenuArray(target)
{
	// the item in question
	var item = getElementByIdOrElement(target);

	// This is a list of the indices in open_menus that will be removed
	var arr_remove = new Array();
	
	// Loop over the contents of open_menus
	for(var i=0; i<open_menus.length; i++) {

		// If this array element has the same id as the item to be removed
		if( open_menus[i].id == item.id ) {

			// Remember this index for later
			arr_remove.push(i);
		}
	}

	// Loop over the array of indices to remove	
	for(var i=0; i<arr_remove.length; i++) {
		
		// Remove this specified index from open_menus
		open_menus.splice(i, 1);
	}
}

/**
 * Close all the menu threads that don't end in this target
 * @param {Object} exception
 */
function closeOtherMenuThreads(link_element) 
{
	// the link parent
	var link_parent = getElementByIdOrElement(link_element['at_parent']);
	
	// The link child
	var link_child = getElementByIdOrElement(link_element['at_child']);
	
	// Get the container surrounding the link parent
	var parent_container = link_parent.parentNode;
	
	// Loop over the elements in the array of open menu items
	for(var i=0; i<open_menus.length; i++) {
		
		// Menu link parent
		menu_parent = getElementByIdOrElement( open_menus[i]['at_parent'] );
		
		// Menu link child
		menu_child = getElementByIdOrElement( open_menus[i]['at_child'] );
		
		// Get the container that contains this menu item link
		menu_parent_container = menu_parent.parentNode;
		
		// If the menu link and the test link parents are both in the same container, and this isnt the exception link
		if( parent_container.id == menu_parent_container.id && link_child.id != menu_child.id ) {

			// make this element hidden
			at_hide_aux(open_menus[i]);
		}
	}
}

/**
 * Determine if a given element is a parent of the target element
 * @param {Object} target
 */
function isAncestor(potential_ancestor, child)
{
	// get the input objects, even if the values are IDs
	var potential_ancestor = getElementByIdOrElement(potential_ancestor);
	var child = getElementByIdOrElement(child);

	// Initialize testElement with the child
	var testElement = child;
	
	// While testElement is valid and its not the HTML tag
	while( testElement && testElement.tagName !== 'HTML' ) {

		// If the test element matches the  potential_ancestor, then return true
		if (testElement == potential_ancestor) {
			return true;
		}

		// Else, set the test element to be the parent of this test element and reiterate
		else {
			testElement = testElement.parentNode;
		}
	}

	// If the loop completes without a match, return false	
	return false;
}

/**
 * Return an object, given either the object itself or it's ID
 * @param {Object} element
 */
function getElementByIdOrElement( element ) 
{
	// the candidate item
	var e = element;
	if( typeof(e) != 'object') {
		e = document.getElementById(element);
	}
	return e;
}

