/***********************************************************************

 $Id: qsextmenus.js,v 1.1.1.1 2003/10/06 10:57:31 www Exp $

Copyright (c) Quadstone 2001, 2002

************************************************************************

Light menu code for External Website. Currently works on recent versions
of IE5, IE6, Netscape 4, Mozilla 0.9, Netscape 6, Opera 5.

Designed not to use *too* much DHTML!

NEW: IE5/6 gets cool shadow effects using proprietary CSS effects. Yay!

***********************************************************************/

// We'll encapsulate the inforation extracted from the HTML in
// the following QSXMenu class.
function QSXMenu(id) {
  this.ID = id;         // Menu ID (used to refer to stuff inside the HTML)
	this.layer = null;    // Used to reference containing layer of the menu
  this.visible = false; // Current visibility
  this.width = null;    // Width of menu (used for positioning submenus)
  this.height = null;
  this.shadow = null;   // Shadow layer (if applicable)
}

// And stick all of the menu-related variables inside a wee
// object, for no real reason :-)
var QSXMenus = new Object();

// Top-level Menus Positioning - this depends on which sort of
// page we're in.
var homepos = new Object();
homepos.abt = new Array(150,130);
homepos.sol = new Array(232,130);
homepos.sys = new Array(304,130);
homepos.svc = new Array(382,130);
homepos.cus = new Array(454,130);
homepos.inf = new Array(542,130);
homepos.cnt = new Array(639,130);

var sublevelpos = new Object();
sublevelpos.sol = new Array(150,130);
sublevelpos.cus = new Array(234,130);
sublevelpos.sys = new Array(311,130);
sublevelpos.abt = new Array(467,130);
sublevelpos.svc = new Array(382,130);
sublevelpos.inf = new Array(542,130);
sublevelpos.cnt = new Array(639,130);

QSXMenus.positions = new Object();
QSXMenus.positions.home = homepos;
QSXMenus.positions.sub  = sublevelpos;

// Mouseover Arrows
QSXMenus.rightArrow = "/images/fwd.gif";
QSXMenus.blankImage = "/images/blank.gif";

// Keep tally of y-offsets to help position submenus
QSXMenus.itemOffsets = new Object();

// Build up an array/object of the IDs of each menu as the script
// encounters them. We'll also make a slightly pointless method
// for getting each menu object from its ID.
QSXMenus.menus = new Array();
QSXMenus.getMenuFromID = function(id) { return QSXMenus.menus[id]; } 

// Keep tally of which and how many menus are currently on screen
QSXMenus.shownMenus = new Array();
QSXMenus.shownMenusSize = 0;

// How long should menus stay up for before closing once the 
// mouse has left them?
QSXMenus.delay = 5000; // (ms)

// ID returned by setTimeout() for doing delayed mouseout
QSXMenus.mouseOutID = null;

/***********************************************************************
// readMenuFromHTML() function extracts salient info from the HTML in
// order to create a Menu object containing the relevant stuff
// corresponding to the given ID. It adds newly formed object to menus
// array, and returns object to caller. Will return previously stored
// copy if called more than once.
*/
QSXMenus.readMenuFromHTML = function(id) {
  var menu, links;
  var i, size;
  if (QSXMenus.getMenuFromID(id)) // Already done this!
    return QSXMenus.getMenuFromID(id);

  // Create object to hold stuff
  menu = QSXMenus.menus[id] = new QSXMenu(id);

	// Get references to the layers making up the menu, relative to the
  // browsers' DOM
	menu.layer = new QSLayer("QX"+id);
	if (!menu.layer) {
		QSCore.debug("Couldn't find one or more layers for menu "+id, 1);
    return null;
	}
  // How wide?
  size = menu.layer.getSize();
  menu.width = size[0];
  menu.height = size[1];

  // Read each item directly from the HTML,
  // setting mouseover and mouseout events for them
  links = menu.layer.getLinks();
  for (i=0; i<links.length; i++) {
    var link = links[i];
    // The HTML should have the action for item defined in the form
    // javascript://menupath
    // or otherwise be a plain URL.
    // We'll mangle this appropriately
    if (link.href.substr(0,13)=="javascript://") {
      var menupath = link.href.substr(13);
      // OK then, some menu(s) should be opened when we mouseover so 
      // set things up correctly.
      // Note that the menu(s) is/are specified as a sort of file
      // path so we should split it up.
      link.href = "javascript://";
      link.onmouseover = new Function("window.status='';"
                                      +"QSXMenus.setArrowImageVisible('"+id+"',"+i+",true);"
                                      +"return QSXMenus.show('" +menupath+"')"
                                      );
      // Get position of icon associated with this item
      // so that we can work out how much vertical offset is
      // required for the submenu.
      //menu.layer.setVisible(true);
      var iconpos = menu.layer.getIconPos("QI"+id+i);
      if (iconpos) {
        var offsetY = iconpos[1] - menu.layer.getPageY();
        QSCore.debug("Setting offset of " + menupath + " to " + offsetY);
        QSXMenus.itemOffsets[menupath] = offsetY;
      }
      else {
        QSCore.debug("Exception in getIconPos()", 1);
      }
    }
    else {
      // Simple URL - install dummy mouseover handler to keep things
      // in check
      link.onmouseover = new Function("QSXMenus.overLink('"+id+"',"+i+")");
    }
    link.onmouseout  = new Function("QSXMenus.outAll('"+id+"',"+i+")");
  }

  // Mozilla/Netscape 6 bug-fix
  //
  // Testing on NS6.1 shows that when we move a table, it loses any
  // padding specified by the cellpadding attr. So we have to explicitly
  // pad each cell. There are also background problems with nbsp-containing
  // cells. Fixed in NS6.2.
  // We'll apply this bug-fix to Gecko browsers dated < 20011001. 
  if (is.Moz) {
    if (navigator.userAgent.match(/Gecko\/(\d+)/) &&
        (parseInt(RegExp.$1)<20011001)) {
      var table, td, i, pad;
      table = menu.layer.ref.getElementsByTagName("table")[0];
      if (table) {
        pad = table.getAttribute("cellpadding");
        td  = table.getElementsByTagName("td");
        if (td) {
          for (i=0; i<td.length; i++) {
            td[i].style.padding = pad+"px";
            if (i%3==2) td[i].style.fontSize = "8pt";
          }
        }
      }
    }
  }
  // Finally, create "shadow" menu, maybe. Depends on browser :-)
  // IE5 creates a nice customised shadow menu with blurry effects (ooooh!)
  // for each menu.
  //
  // Mozilla/Opera: similar but more crap
  //
  // Netscape 4: has to make do with resizing precreated shadow layers
  // as creating layers dynamically increases the probability of crashes
  if (is.IE5) {
    var str = '<div id="QS'+id+'" '
      + 'style="position:absolute;width:'
      + (size[0]+10) + 'px;height:'
      + (size[1]+10) + 'px;'
      + 'filter:blur(direction=135,strength=3) '
      + 'blur(direction=135, strength=3) '
      + 'blur(direction=135, strength=2) '
      + 'blur(direction=135, strength=2) '
      + '">'
      + '<table border="0" cellpadding="0" cellspacing="0">'
      + '<tr><td style="background:url(\'/images/menuDot.gif\')">'
      + '<img src="/images/blank.gif" width="'+size[0]+'"'
      + ' height="'+size[1]+'" alt=""></td></tr></table></div>';
    menu.layer.ref.insertAdjacentHTML("AfterEnd", str);
    menu.shadow = new QSLayer("QS"+id);
    menu.shadow.setVisible(true);
  }
  else if (is.Moz) {
    // Create new Layer element
    var e = document.createElement("div");
    e.setAttribute("id", "QS"+id);
    e.setAttribute("style","position:absolute;background:black;width:"
                   + size[0] + "px;height:" + size[1] + "px");
    document.getElementsByTagName("body")[0].appendChild(e);
    menu.shadow = new QSLayer("QS"+id);
  }
  return menu;
}

// The link() function is a dummy used to encode what's going
// to be happening with the menus. 
QSXMenus.link = function() {
  return false;
}

// show() is called by the mouseover events installed above
// when it's time to a display a top-level menu (and
// possibly some of its submenus as well).
QSXMenus.show = function(path) {
  // First, cancel any pending delayed mouseouts
  QSXMenus.cancelOut();
  QSCore.debug("show "+path, 10);
  
  // Work out which menu(s) are to be displayed.
  // This'll be specified as a pseudo UNIX file path
  // e.g. menu1/menu2 or something. Do NOT use
  // leading and trailing slashes :-)
  var menuPath;
  if (!is.Opera)
    menuPath = path.split(/\//);
  else
    menuPath = QSCore.splitString(path,"/");

  // Clear current menus
  var i;
  for (i in QSXMenus.shownMenus) {
    if (menuPath[i]!=QSXMenus.shownMenus[i]) {
      QSXMenus.hide(i);
      break;
    }
  }
  // Work out initial position, according to page style
  // and first menu in path
  if (isHomePage) {
    p = QSXMenus.positions.home;
  }
  else {
    p = QSXMenus.positions.sub;
  }
  var x,y;
  var x = p[menuPath[0]][0];
  var y = p[menuPath[0]][1];
  var z = 0;
  
  // Now display each menu
  var pathstr = "";
  var id, menu;
  for (i in menuPath) {
    // Work out y-offset for menu
    if (i>0) pathstr += "/";
    pathstr += menuPath[i];
    if (QSXMenus.itemOffsets[pathstr]) {
      QSCore.debug("Found offset "  + QSXMenus.itemOffsets[pathstr]);
      y += QSXMenus.itemOffsets[pathstr];
    }
    else {
      QSCore.debug("No y-offset for string '"+pathstr+"'", 10);
    }
    id = menuPath[i];
    menu = QSXMenus.display(id,x,y,z++);
    
    // Work out x-offset for next menu
    x += menu.width;
  }
  // Set ours as current menu
  for (i=0; i<menuPath.length; i++) {
    QSXMenus.shownMenus[i] = menuPath[i];
  }
  QSXMenus.shownMenusSize = menuPath.length;
  window.status = '';
  return true;
}

// display() is used by show() to do the actual task of
// making menus visible.
// (x,y) is the desired position of the top left corner of the
// menu.
// Returns ref to menu object so caller can work out where
// to position any submenus
QSXMenus.display = function(id,x,y,z) {
  var newx;
  QSCore.debug("display("+id+","+x+","+y+","+z+")");
  // Get menu object from HTML (if necessary)
  var menu = QSXMenus.readMenuFromHTML(id);
  if (menu==null) {
    return;
  }
  // Is menu already visible?
  if (menu.visible)
    return menu;

  // Move to correct position (menus are allowed to move after being
  // created) Should really make sure the menu can fit in the page
  // though, so here's a bit of fudge. It's not perfect though.
  QSCore.debug("x="+x+" y="+y);

  // Fudge for home page - keep away from Flash
  if (isHomePage && x+menu.width >= 465 && y <= 300) {
    y = 300 + (y-250)/10;
  }

  // Make sure shadow layer is prepared. For Netscape 4, we'll
  // have to resize it to fit
  if (is.NS4) {
    menu.shadow = new QSLayer("QH"+z);
    menu.shadow.setSize(menu.width, menu.height);
    // Displace shadow slightly 
    menu.shadow.moveTo(x+2, y+2);
  }
  else if (is.Moz && menu.shadow) { // Mozilla version
    menu.shadow.moveTo(x+2, y+2);
  }
  else if (is.IE5 && menu.shadow) { // IE version
    menu.shadow.moveTo(x,y);
  }

  // Move to correct position
	menu.layer.moveTo(x,y);

  // Set stacking
  menu.layer.setZIndex(2*(2-z)+1);
  if (menu.shadow) menu.shadow.setZIndex(2*(2-z));

  // Make visible
  menu.layer.setVisible(true);
  if (menu.shadow) menu.shadow.setVisible(true);

  // Probably not needed, but just in case...
  if (is.NS4) {
    if (x+menu.width>window.document.width)
      window.document.width = x+menu.width;
    if (y+menu.height>window.document.height)
      window.document.height = y+menu.height;
  }
  return menu;
}

// Hide all displayed menus from given starting location
QSXMenus.hide = function(start) {
  var i;
  for (i=start; i<QSXMenus.shownMenusSize; i++) {
    var menu = QSXMenus.getMenuFromID(QSXMenus.shownMenus[i]);
    menu.visible = false;
    menu.layer.setVisible(false);
    if (menu.shadow) menu.shadow.setVisible(false);
  }
  QSXMenus.shownMenusSize = start;
}

/************************************************************

Event Handling Code

************************************************************/

// overLink called when we go over an item with no submenu. 
// Cancel all menus lower in the path than current (if there's
// a match). Otherwise do nothing (except cancel any impending
// mouseout events and set mouseover image)
QSXMenus.overLink = function(menuID, item) {
  var i;
  for (i=0; i<QSXMenus.shownMenusSize-1; i++) {
    var id = QSXMenus.shownMenus[i];
    if (id==menuID) {
      QSXMenus.hide(i+1);
      break;
    }
  } 
  QSXMenus.setArrowImageVisible(menuID, item, true);
  return QSXMenus.cancelOut();
}

QSXMenus.setArrowImageVisible = function(menuID, item, bool) {
  // Do mouseover arrow image
  QSCore.debug("setArrowImageVisible("+menuID+","+item+","+bool+")");
  if (menuID) {
    var menu = QSXMenus.getMenuFromID(menuID);
    var icon = menu.layer.getIcon("QI"+menuID+item);
    icon.src = (bool) ? QSXMenus.rightArrow : QSXMenus.blankImage;
  }
}

// Called on mouseout event. Sets delayed cancelling of *all* menus.
QSXMenus.outAll = function(menuID, item) {
  // Hide current mouseover icon, if applicable
  if (menuID)
    QSXMenus.setArrowImageVisible(menuID, item, false);

  // Cancel pending mouseouts
  if (QSXMenus.mouseOutID) { // Only take last mouseOut
    window.clearTimeout(QSXMenus.mouseOutID);
  }
  // Install new timeout
  QSXMenus.mouseOutID = window.setTimeout("QSXMenus.delayedMouseOut()",
                                          QSXMenus.delay);
  window.status = '';
  return true;
}

QSXMenus.cancelOut = function() {
  QSCore.debug("Cancelling mouseout events");
  // Cancel any delayed mouse out event pending
  if (QSXMenus.mouseOutID) {
    window.clearTimeout(QSXMenus.mouseOutID);
    QSXMenus.mouseOutID = null;
  }
  return true;
}

// delayedMouseOut() gets called a pre-determined amount of time after
// the mouse moves outside of all menus.  This is to allow for
// possible nice things to happen in the interim period and to account
// for browsers which don't have very good granularity in their mouse
// tracking.  This makes things a bit complicated as things can happen
// during this intervening period. First, we should check that this
// event hasn't been cancelled by an over() event happening
// afterwards. (This shouldn't happen now as I've changed the
// cancelling so it never gets here!)
QSXMenus.delayedMouseOut = function() {
  QSCore.debug("delayedMouseOut() has been called", 10);
  // Still a mouseout event waiting?
  if (!QSXMenus.mouseOutID) {
    QSCore.debug("qsmenus.js exception in delayedMouseOut()", 1);
  }
  var i;
  QSXMenus.hide(0);
}

// Before leaving, we need to capture the relevant mouse events 

// First, for Netscape, need to explicitly capture all
// mouse up events in the document.
if (is.NS4) {
  document.captureEvents(Event.MOUSEUP);
}

// Now set up the correct event handler to allow user to
// get rid of menus by clicking with mouse button.
// (NB: Opera doesn't really support this yet. AFAIK)
// This'll only do something if a delayed mouseout event is queued up.
document.onmouseup = function() {
  if (QSXMenus.mouseOutID) {
    QSXMenus.hide(0);
    return true;
  }
}

