      /*
       * Flyout menus for the University of Washington Home Page
       * University of Washington / Computing and Communications
       * May, 2003
       * Documentation can be found at
       *     http://www.washington.edu/webinfo/case/flyout/
       * $Id: flyout.js,v 1.52 2003/05/13 17:38:08 fmf Exp $
       *
       * You are free to copy and/or use these flyout menus (or
       * derivative works) but please make sure this comment block
       * remains intact and in its entirety at the top of the file.
       */

      var d = document;
      var doDHTML = 0;
      d.versIE = 0;

      if ((! d.cssonly && d.layers) || d.all || d.getElementById) {
          var vers = navigator.appVersion.split ('MSIE ');
          vers = vers[vers.length - 1];
          d.versIE = parseInt (vers);
          FlyLyr.isMac = navigator.platform.indexOf ('Mac') >= 0;
          if (navigator.appVersion.indexOf ('MSIE') < 0 || ! FlyLyr.isMac || d.versIE >= 5) {
              doDHTML = 1;
              FlyLyr.isOpera = navigator.userAgent.indexOf (' Opera ') >= 0;
              FlyLyr.isKonq = navigator.userAgent.indexOf (' Konqueror') >= 0;

              initFlyLyr ();
              initToShow ();
              initDelay ();

              d.write ('<style type="text/css">' +
                          '.flyout { visibility: hidden; position: absolute; ' +
                          'left: 0; top: 0; margin-right: -15px; margin-bottom: -15px; }' +
                          '.flyoutindent {font-size: xx-small; }<\/style>');
          }
      }

      function useLayer (id) {
          if (! doDHTML || ! d.body.appendChild)
              return;
          var elem = findObj ('l_' + id);
          if (! elem)
              return;
          if (FlyLyr.defs.preDetach)
              FlyLyr.defs.preDetach (elem);
          if (elem.parentNode.tagName != 'BODY')
              d.body.appendChild (elem.parentNode.removeChild (elem));
          new FlyLyr (id, 0);
      }

      function makeLayer (id, title) {
          if (! doDHTML)
              return;
          var a = arguments;
          var numsp = new Array;
          numsp.max = new Array;
          var pos = 0;
          numsp.max[0] = 0;
          for (var j = 2; j < a.length; ++j) {
              numsp[j] = a[j].length;
              if (numsp[j]) {
                  a[j] = a[j].replace (/^ +/, '');
                  numsp[j] -= a[j].length;
                  numsp.max[pos] = Math.max (numsp[j], numsp.max[pos]);
              } else
                  numsp.max[++pos] = 0;
          }
          var fd = FlyLyr.defs;
          d.write ('<div id="l_' + id + '" class="flyout">' +  
                      '<table cellpadding="2" cellspacing="0" border="1" bgcolor="' +
                      fd.background + '" bordercolor="' + fd.border + '">');
          if (title) {
              var titleargs = 'align="center"';
              if (fd.titlebackground)
                  titleargs += ' bgcolor="' + fd.titlebackground + '"';
              d.write ('<tr>' +
                          buildCell (title,
                                      fd.titleclass ? fd.titleclass : fd.useclass,
                                      titleargs) + '<\/tr>');
          }
          var divstart = '<tr><td><table cellpadding="1" cellspacing="0" ' +
                          'border="0">' + "\n";
          d.write (divstart);
          pos = 0;
          for (j = 2; j < a.length; ++j) {
              if (! a[j]) {
                  d.write ('<\/table><\/td><\/tr>' + divstart);
                  ++pos;
                  continue;
              }
              var args = null;
              var i = numsp[j];
              if (i < numsp.max[pos])
                  args = 'colspan="' + (numsp.max[pos] - i + 1) + '"';
              if (fd.alignright)
                  args += ' align="right"';
              d.write ('<tr>');
              while (i-- > 0)
                  d.write ('<td class="flyoutindent">&nbsp;&nbsp;<\/td>');
              d.write (buildCell (a[j], fd.useclass, args) + '<\/tr>' + "\n");
          }
          d.write ('<\/table><\/td><\/tr><\/table><\/div>' + "\n");
          new FlyLyr (id, 1);
      }

      function buildCell (str, fc, args) {
          var retstr = '<td class="' + fc + '"';
          if (args)
              retstr += ' ' + args;
          retstr += '>';
          var eqpos = str.indexOf ('=');
          var atpos = str.indexOf ('@');
          if (eqpos > 0) {
              if (atpos > eqpos)
                  retstr += '<a href="' + str.substr (eqpos + 1, atpos - eqpos - 1)
                              + '" target="' + str.substr (atpos + 1) + '">';
              else
                  retstr += '<a href="' + str.substr (eqpos + 1) + '">';
              retstr += str.substr (0, eqpos) + '<\/a>';
          } else
              retstr += str;
          return retstr + '<\/td>';
      }

      function positionLayer () {
          var img = this.image;
          this.getObjMetrics (img);
          this.normalizeVars ();
          var xpos = img.width + this.hpad;
          if (this.positionleft)
              xpos = -this.lyr.offsetWidth - this.hpad;
          xpos += img.flyX;
          var ypos = img.flyY + this.vpad;
          if (this.position) {
              var strs = this.position.split (';');
              for (var i = 0; i < strs.length; ++i) {
                  var str = strs[i];
                  if ((pos = str.search (/[-|]/)) <= 0)
                      continue;
                  var direct = str.substr (pos, 1);
                  var obj = img;
                  if (str.substr (0, pos) != 'IMG') {
                      if (! (obj = findObj (str.substr (0, pos), d)))
                          continue;
                      this.getObjMetrics (obj);
                  }
                  var posstr = str.substr (pos + 1);
                  var cmp = posstr.search (/[<=>]/);
                  if (cmp <= 0)
                      continue;
                  var opos, mpos;
                  if (direct == '-') {
                      opos = targetPos (posstr.substr (0, cmp), obj.flyX, obj.width);
                      mpos = targetPos (posstr.substr (cmp + 1), xpos, this.lyr.offsetWidth);
                  } else {
                      opos = targetPos (posstr.substr (0, cmp ), obj.flyY, obj.height);
                      mpos = targetPos (posstr.substr (cmp + 1), ypos, this.lyr.offsetHeight);
                  }
                  var rel = posstr.substr (cmp, 1);
                  if ((rel == '<' && mpos < opos) || (rel == '>' && mpos > opos) || rel == '=') {
                      if (direct == '-')
                          xpos += opos - mpos;
                      else
                          ypos += opos - mpos;
                  }
              }
          }
          xpos = posInWindow (xpos, this.lyr.offsetWidth, window.pageXOffset, window.innerWidth);
          ypos = posInWindow (ypos, this.lyr.offsetHeight, window.pageYOffset, window.innerHeight);
          this.moveTo (xpos, ypos);
          if (this.tbl)
              this.lyr.style.clip = 'rect ( 0px ' + this.tbl.offsetWidth + 'px ' +
                                      this.tbl.offsetHeight + 'px 0px )';
      }

      function getObjMetricsIE (obj) {
          var oObj = obj;
          oObj.width = obj.offsetWidth || obj.width;
          oObj.height = obj.offsetHeight || obj.height;
          oObj.flyX = oObj.flyY = 0;
          var seenTable = 0;
          if (FlyLyr.isMac && oObj.offsetParent.tagName == 'BODY') {
              if (getInt (oObj.clientLeft) + getInt (oObj.clientTop)) {
                  oObj.flyX = oObj.clientLeft;
                  oObj.flyY = oObj.clientTop;
              } else {
                  oObj.flyX = oObj.offsetLeft;
                  oObj.flyY = oObj.offsetTop;
              }
              return;
          }
          for (; obj; obj = obj.offsetParent) {
              var tag = obj.tagName;
              if (! FlyLyr.noCpos[tag] && (! FlyLyr.isMac || obj != oObj) &&
                      ! FlyLyr.isOpera) {
                  oObj.flyX += getInt (obj.clientLeft);
                  oObj.flyY += getInt (obj.clientTop);
              }
              var noOent = FlyLyr.noOpos[tag];
              if (! noOent || (noOent < 0 && obj.currentStyle &&
                      obj.currentStyle.display != 'block')) {
                  oObj.flyX += getInt (obj.offsetLeft);
                  oObj.flyY += getInt (obj.offsetTop);
              }
              if (FlyLyr.isMac && tag == 'TABLE')
                  if (seenTable++)
                      oObj.flyY += getInt (obj.cellSpacing);
          }
      }

      function getObjMetricsDOM (obj) {
          var oObj = obj;
          obj.width = obj.width || obj.offsetWidth;
          obj.height = obj.height || obj.offsetHeight;
          oObj.flyX = oObj.flyY = 0;
          for (; obj; obj = obj.offsetParent) {
              if (obj.tagName == 'TABLE') {
                  var bord = parseInt (obj.border);
                  if (isNaN (bord)) {
                      if (obj.getAttribute ('frame')) {
                          ++oObj.flyX;
                          ++oObj.flyY;
                      }
                  } else if (bord > 0) {
                      oObj.flyX += bord;
                      oObj.flyY += bord;
                  }
              }
              oObj.flyX += obj.offsetLeft;
              oObj.flyY += obj.offsetTop;
          }
      }

      function getInt (n) {
          n = parseInt (n);
          if (isNaN (n))
              return 0;
          return n;
      }

      function targetPos (wherestr, start, len) {
          var where = wherestr.substr (0, 1);
          var adj = getInt (wherestr.substr (1));
          if (where == 'l' || where == 't')
              return start + adj;
          if (where == 'r' || where == 'b')
              return start + len + adj;
          return start + len / 2 + adj;
      }

      function posInWindow (loc, objSize, scroll, winSize) {
          var move = loc + objSize - scroll - winSize;
          move += FlyLyr.nsOffset;
          if (move > 0)
              loc -= move;
          if (loc < scroll)
              loc = scroll;
          return loc;
      }

      function FlyLyr (id, doclip) {
          this.lyr = findObj ('l_' + id);
          if (! d.all && ! d.getElementById) {
              this.lyr.captureEvents (Event.MOUSEOUT | Event.MOUSEOVER);
              this.lyr.bgColor = FlyLyr.defs.background;
          }
          this.lyr.onmouseover = function () {
              FlyLyr.showing.stopdelay ()
              ToShow.stopdelay ();
          };
          this.lyr.onmouseout = function () { mOut () };
          this.id = id;
          if (doclip && this.lyr.children && ! FlyLyr.isMac)
              this.tbl = this.lyr.children[0];
          FlyLyr.lyrs[id] = this;
          for (var a in FlyLyr.defs)
              this[a] = FlyLyr.defs[a];
      }

      function flyDefs (defs) {
          if (! defs)
              return;
          for (var d in defs)
              FlyLyr.defs[d] = defs[d];
      }

      function initFlyLyr () {
          FlyLyr.prototype.doHide = function () {
              this.stopdelay ();
              this.realHide ();
              if (this.hideImage)
                  this.hideImage (this.image, this.lyr);
              else if (this.outimg && this.image.tagName == 'IMG')
                  this.image.src = this.outimg;
              FlyLyr.showing = null;
          };
          FlyLyr.prototype.doShow = function () {
              if (! this.image && ! (this.image = findObj (this.id)))
                  return;
              this.positionLayer ();
              if (! this.outimg && this.image.tagName == 'IMG')
                  this.outimg = this.image.src;
              if (this.showImage)
                  this.showImage (this.image, this.lyr);
              else if (this.overimg && this.image.tagName == 'IMG')
                  this.image.src = this.overimg;
              this.realShow ();
              FlyLyr.showing = this;
          };
          FlyLyr.prototype.delayCallback = function () {
              if (this == FlyLyr.showing)
                  this.doHide ();
          };
          FlyLyr.prototype.setdelay = function () {
              this.delay = new Delay (this.timeout, this);
          };
          FlyLyr.prototype.stopdelay = function () {
              if (this.delay)
                  this.delay.stop ();
              return this.delay;
          };

          FlyLyr.lyrs = new Object ();
          FlyLyr.defs = {
              background: '#ffffff',
              titlebackground: '#333399',
              border: '#333399',
              useclass: 'navlink',
              titleclass: 'barlink',
              overimg: '/home/graphics/mo/arrow.gif',
              outimg: null,
              pause: 250,
              timeout: 1000,
              positionleft: 0,
              alignright: 0,
              hpad: 2,
              vpad: -2,
              position: '',
              preDetach: null,
              showImage: null,
              hideImage: null
          }
          FlyLyr.prototype.positionLayer = positionLayer;
          FlyLyr.nsOffset = 0;

          if (d.all && ! FlyLyr.isKonq) {
              FlyLyr.prototype.realHide = function () {
                  this.lyr.style.visibility = 'hidden';
              };
              FlyLyr.prototype.realShow = function () {
                  this.lyr.style.visibility = 'visible';
              };
              FlyLyr.prototype.getObjMetrics = getObjMetricsIE;
              if (FlyLyr.isOpera)
                  FlyLyr.prototype.normalizeVars = function () {};
              else
                  FlyLyr.prototype.normalizeVars = function () {
                      window.innerWidth = d.body.clientWidth;
                      window.innerHeight = d.body.clientHeight;
                      window.pageXOffset = d.body.scrollLeft;
                      window.pageYOffset = d.body.scrollTop;
                  };
              FlyLyr.prototype.moveTo = function (x, y) {
                  this.lyr.style.pixelLeft = x;
                  this.lyr.style.pixelTop = y;
              };
              FlyLyr.noCpos = {
                  'BODY': 1,
                  'TABLE': 1
              };
              FlyLyr.noOpos = {};
              if (! FlyLyr.isOpera)
                  FlyLyr.noOpos['A'] = -1;
              if (FlyLyr.isMac && ! FlyLyr.isOpera) {
                  FlyLyr.noOpos = FlyLyr.noCpos;
                  FlyLyr.noCpos = {
                      'DIV': 1,
                      'TD': 1,
                      'TH': 1
                  };
              }
          } else if (d.getElementById) {
              FlyLyr.prototype.realHide = function () {
                  this.lyr.style.visibility =  'hidden';
              };
              FlyLyr.prototype.realShow = function () {
                  this.lyr.style.visibility = 'visible';
              }
              FlyLyr.prototype.getObjMetrics = getObjMetricsDOM;
              FlyLyr.prototype.normalizeVars = function () {};
              FlyLyr.prototype.moveTo = function (x, y) {
                  this.lyr.style.left = x + 'px';
                  this.lyr.style.top = y + 'px';
              };
          } else {
              FlyLyr.prototype.realHide = function () {
                  this.lyr.visibility = 'hide';
              };
              FlyLyr.prototype.realShow = function () {
                  this.lyr.visibility = 'show';
              };
              FlyLyr.prototype.getObjMetrics = function (obj) {
                  obj.flyX = obj.x;
                  obj.flyY = obj.y;
              };
              FlyLyr.prototype.normalizeVars = function () {
                  this.lyr.offsetHeight = this.lyr.clip.bottom;
                  this.lyr.offsetWidth = this.lyr.clip.right;
              };
              FlyLyr.prototype.moveTo = function (x, y) {
                  this.lyr.moveTo (x, y);
              };
              FlyLyr.nsOffset = 16;
          }
      }

      function ToShow (lyr) {
          if (! lyr || lyr == FlyLyr.showing)
              return;
          this.lyr = lyr;
          ToShow.stopdelay ();
          ToShow.queued = this;
          this.delay = new Delay (FlyLyr.defs.pause, this);
      }

      function initToShow () {
          ToShow.stopdelay = function () {
              var q = ToShow.queued;
              if (q) {
                  q.delay.stop ();
                  ToShow.queued = null;
              }
              return q;
          };
          ToShow.prototype.delayCallback = function () {
              ToShow.queued = null;
              if (FlyLyr.showing)
                  FlyLyr.showing.doHide ();
              if (this.lyr)
                  this.lyr.doShow ();
          };
      }

      function Delay (delay, obj) {
          this.obj = obj;
          var uid = ++Delay.nuid;
          this.timeoutid = setTimeout ('Delay.dispatch (' + uid + ')', delay);
          this.uid = uid;
          Delay.disparr[uid] = this;
      }

      function initDelay () {
          Delay.prototype.stop = function () {
              clearTimeout (this.timerid);
              delete Delay.disparr[this.uid];
          };
          Delay.dispatch = function (uid) {
              var item = Delay.disparr[uid];
              if (! item)
                  return;
              item.obj.delayCallback ();
              item.stop ();
          };
          Delay.nuid = 0;
          Delay.disparr = new Object;
      }

      function findObj (n, od) { // based on MM_findObj v3.0 from Macromedia Dreamweaver
          var p, i, x;
          if (! od)
              od = d;
          if ((p = n.indexOf ('?')) > 0 && parent.frames.length) {
              od = parent.frames[n.substring (p + 1)].document;
              n = n.substring (0, p);
          }
          if (! (x = od[n]) && d.all)
              x = od.all[n];
          if (! x && d.getElementById)
              x = d.getElementById (n);
          for (i = 0; ! x && i < od.forms.length; i++)
              x = od.forms[i][n];
          for (i = 0; ! x && od.layers && i < od.layers.length; i++)
              x = findObj (n, od.layers[i].document);
          return x;
      }

      function mIn (id) {
          if (! doDHTML)
              return;
          var lyr = FlyLyr.lyrs[id];
          if (! lyr) {
              if (findObj ('l_' + id))
                  useLayer (id);
              lyr = FlyLyr.lyrs[id];
              if (! lyr)
                  return;
          }
          if (lyr == FlyLyr.showing)
              lyr.stopdelay ();
          else
              new ToShow (lyr);
      }

      function mOut () {
          if (! doDHTML)
              return;
          var lyr = FlyLyr.showing;
          if (! ToShow.stopdelay () && lyr)
              lyr.setdelay ();
      }
