   // return string w/out HTML tags or w/ unescaped single quotes
function getTrueString(str, isHtml)
{
   return isHtml ? str.replace(/<[^>]*>/gi, '') : str.replace(/\\'[']/, '$1, $2');
}

   // use with 'event.keyCode'
function getKey(keyCode)
{
   switch(keyCode)
   {
      case  27: return 'escape';

      case 112: return 'f1';
      case 113: return 'f2';
      case 114: return 'f3';
      case 115: return 'f4';
      case 116: return 'f5';
      case 117: return 'f6';
      case 118: return 'f7';
      case 119: return 'f8';
      case 120: return 'f9';
      case 121: return 'f10';
      case 122: return 'f11';
      case 123: return 'f12';

      case  19: return 'pause';
      case  44: return 'printscreen';
      case 145: return 'scrolllock';

      case   8: return 'backspace';
      case  45: return 'insert';
      case  46: return 'delete';
      case  36: return 'home';
      case  35: return 'end';
      case  33: return 'pageup';
      case  34: return 'pagedown';

      case  37: return 'left';
      case  38: return 'up';
      case  39: return 'right';
      case  40: return 'down';

      case  13: return 'enter';
      case  20: return 'capslock';
      case   9: return 'tab';
      case  32: return 'space';

      default:  return null;
   }
}

   // tnx to David Andersson <http://liorean.web-graphics.com/>
function getIFrameDocRef(ref)
{
   return (typeof ref.contentDocument != 'undefined') ? ref.contentDocument :
          (typeof ref.contentWindow   != 'undefined') ? ref.contentWindow.document :
          (typeof ref.document        != 'undefined') ? ref.document :
          null;
}

   // thanks to Paul Novitsky paul@novitskisoftware.com
function getElementsByClassName(argClassName, argTagName)
{
   if (document.getElementsByClassName)
   // use native implementation
      return argTagName ? argTagName.getElementsByClassName(argClassName) : document.getElementsByClassName(argClassName);

// requested class name, optionally bracketed by spaces to handle multiple classes
   var reClassMatch = new RegExp('(^| )' + argClassName + '( |$)');

   var aResult = [];

   var sTagName = '*';
   if (argTagName)
      sTagName = argTagName;

   var aEls = document.getElementsByTagName(sTagName);
   for (var i = 0; i < aEls.length; i++)
      if (reClassMatch.test(aEls[i].className))
         aResult.push(aEls[i]);

   if (aResult.length == 0)
      aResult = null;

   return aResult;
}

   // tnx to Simon Willison <http://www.sitepoint.com/blog-post-view.php?id=171578>
function addLoadEvent(fn)
{
   var oldonload = window.onload;
   if (typeof window.onload == 'function')
      window.onload = function() { oldonload(); fn(); };
   else
      window.onload = fn;
}

function fixEvent(event)
{
   event.preventDefault  = fixEvent.preventDefault;
   event.stopPropagation = fixEvent.stopPropagation;
   event.target          = event.srcElement;
   return event;
}
fixEvent.preventDefault  = function() { this.returnValue = false; };
fixEvent.stopPropagation = function() { this.cancelBubble = true; };

function sendToDevNull(event)
{
   event = event || fixEvent(window.event);
   event.preventDefault();
   event.stopPropagation();
   return false;
}

function getXploderEvent()
{
/* due to micro$uxx' inane desire to do everything differently, the event is a property of 'window' rather than a global object;
   so, since a frame is a window object, to query events from frame A in frame B or from an iFrame *outside the iFrame* I first
   have to figger out to which frame the desired event belongs [sigh...]
*/
   return fixEvent(document.parentWindow.event);

   if (BrowserDetect.browser == 'explorer' && BrowserDetect.version < 6)    // 'Object doesn't support this property or method' -> top.frames[i].event
      return fixEvent(window.event);

   for (var i = 0; i < top.frames.length; i++)
      if (top.frames[i].event)
         return fixEvent(top.frames[i].event);

   return fixEvent(window.event);
}

   // flwg function removes spurious textnodes & comments in w3c compliant browsers [tnx to John Resig]
function cleanWhitespace(element)
{
   element = element || document;

   var temp;
   var currentElement = element.firstChild;
   while (currentElement != null)
   {
      temp = currentElement.nextSibling;

   // the node is a text node and contains nothing but whitespace or is a comment
      if (currentElement.nodeType == 3 && !/\S/.test(currentElement.nodeValue) || currentElement.nodeType == 8)
         element.removeChild(currentElement);
      else if (currentElement.nodeType == 1)
         cleanWhitespace(currentElement);

      currentElement = temp;
   }
}

function getScreenX(windowObj)
{
   windowObj = windowObj || window;

   return windowObj.Left ? windowObj.Left : screenX;
}

function getScreenY(windowObj)
{
   windowObj = windowObj || window;

   return windowObj.Top ? windowObj.Top : screenY;
}

function getOuterWidth(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.outerWidth)
      return windowObj.outerWidth;
   else if (windowObj.document.documentElement && windowObj.document.documentElement.offsetWidth)
      return windowObj.document.documentElement.offsetWidth;
   else if (windowObj.document.body)
      return windowObj.document.body.offsetWidth;

   return null;
}

function getOuterHeight(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.outerHeight)
      return windowObj.outerHeight;
   else if (windowObj.document.documentElement && windowObj.document.documentElement.offsetHeight)
      return windowObj.document.documentElement.offsetHeight;
   else if (windowObj.document.body)
      return windowObj.document.body.offsetHeight;

   return null;
}

   // flwg 6 functions tnx to <http://www.quirksmode.org/viewport/compatibility.html>
function getInnerWidth(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.innerWidth)
      return windowObj.innerWidth;
   else if (windowObj.document.documentElement && windowObj.document.documentElement.clientWidth)
      return windowObj.document.documentElement.clientWidth;
   else if (windowObj.document.body)
      return windowObj.document.body.clientWidth;

   return null;
}

function getInnerHeight(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.innerHeight)
      return windowObj.innerHeight;
   else if (windowObj.document.documentElement && windowObj.document.documentElement.clientHeight)
      return windowObj.document.documentElement.clientHeight;
   else if (windowObj.document.body)
      return windowObj.document.body.clientHeight;

   return null;
}

function getPageXOffset(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.pageXOffset)
      return windowObj.pageXOffset;
   else if (windowObj.document.documentElement && windowObj.document.documentElement.scrollLeft)
      return windowObj.document.documentElement.scrollLeft;
   else if (windowObj.document.body)
      return windowObj.document.body.scrollLeft;

   return null;
}

function getPageYOffset(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.pageYOffset)
      return windowObj.pageYOffset;
   else if (windowObj.document.documentElement && windowObj.document.documentElement.scrollTop)
      return windowObj.document.documentElement.scrollTop;
   else if (windowObj.document.body)
      return windowObj.document.body.scrollTop;

   return null;
}

function getPageWidth(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.document.documentElement)
   {
      if (windowObj.document.documentElement.offsetWidth < windowObj.document.documentElement.scrollWidth)
         return windowObj.document.documentElement.scrollWidth;
      else
         return windowObj.document.documentElement.offsetWidth;
   }
   else
   {
      if (windowObj.document.body.offsetWidth < windowObj.document.body.scrollWidth)
         return windowObj.document.body.scrollWidth;
      else
         return windowObj.document.body.offsetWidth;
   }

   return null;
}

function getPageHeight(windowObj)
{
   windowObj = windowObj || window;

   if (windowObj.document.documentElement)
   {
      if (windowObj.document.documentElement.offsetHeight < windowObj.document.documentElement.scrollHeight)
         return windowObj.document.documentElement.scrollHeight;
      else
         return windowObj.document.documentElement.offsetHeight;
   }
   else
   {
      if (windowObj.document.body.offsetHeight < windowObj.document.body.scrollHeight)
         return windowObj.document.body.scrollHeight;
      else
         return windowObj.document.body.offsetHeight;
   }

   return null;
}

   // flwg 2 function tnx to <http://www.quirksmode.org/js/events_properties.html>
   // adapted for xPloder 6 'strict' mode
function getMouseX(event)
{
   event = event || window.event;

   if (event.pageX)
      return event.pageX;
   else if (event.clientX && document.documentElement && document.documentElement.scrollLeft)
      return event.clientX + document.documentElement.scrollLeft;
   else if (event.clientX && document.body)
      return event.clientX + document.body.scrollLeft;

   return null;
}

function getMouseY(event)
{
   event = event || window.event;

   if (event.pageY)
      return event.pageY;
   else if (event.clientY && document.documentElement && document.documentElement.scrollTop)
      return event.clientY + document.documentElement.scrollTop;
   else if (event.clientY && document.body)
      return event.clientY + document.body.scrollTop;

   return null;
}

   // flwg 2 functions tnx to <http://www.quirksmode.org/js/findpos.html>
function getPosX(obj)
{
   var trueLeft = 0;
   while (obj.offsetParent)
   {
      trueLeft += obj.offsetLeft;
      obj       = obj.offsetParent;
   }
   return trueLeft;
}

function getPosY(obj)
{
   var trueTop = 0;
   while (obj.offsetParent)
   {
       trueTop += obj.offsetTop;
       obj      = obj.offsetParent;
   }
   return trueTop;
}

   // flwg 2 functions prevent popups and tooltips from disappearing under viewport edges
function fitObjInViewportWidth(Xpos, obj, windowObj)
{
   windowObj = windowObj || window;

   var totalWidth = Xpos + obj.offsetWidth;
   var pageWidth  = getPageXOffset(windowObj) + getInnerWidth(windowObj) - getScrollbarWidth(windowObj);

   var newXpos = Xpos;
   if (pageWidth < totalWidth)
      newXpos = pageWidth - obj.offsetWidth;     // correct for right underlap
   if (newXpos < getPageXOffset(windowObj))
      newXpos = getPageXOffset(windowObj);       // correct for left underlap

   return newXpos;
}

function fitObjInViewportHeight(Ypos, obj, windowObj)
{
   windowObj = windowObj || window;

   var totalHeight = Ypos + obj.offsetHeight;
   var pageHeight  = getPageYOffset(windowObj) + getInnerHeight(windowObj) - getScrollbarWidth(windowObj);

   var newYpos = Ypos;
   if (pageHeight < totalHeight)
      newYpos = pageHeight - obj.offsetHeight;   // correct for bottom underlap
   if (newYpos < getPageYOffset(windowObj))
      newYpos = getPageYOffset(windowObj);       // correct for top underlap

   return newYpos;
}

function getScrollbarWidth(windowObj)
{
   windowObj = windowObj || window;

   return BrowserDetect.browser == 'explorer'   // xPloder is unreliable in quirks mode: document.body.offsetWidth does not change when a scrollbar is necessitated by the doc's width
          ? windowObj.document.documentElement
             ? document.documentElement.offsetWidth - document.documentElement.clientWidth
             : document.body.offsetWidth - document.body.clientWidth
          : windowObj.innerWidth - document.documentElement.clientWidth;
}

function setOpacity(obj, newOpacity)
{
        if ('opacity' in obj.style)      obj.style.opacity      = newOpacity;
   else if ('MozOpacity' in obj.style)   obj.style.MozOpacity   = newOpacity;
   else if ('KhtmlOpacity' in obj.style) obj.style.KhtmlOpacity = newOpacity;
   else if ('filter' in obj.style)       obj.style.filter       = 'alpha(opacity=' + (newOpacity * 100) + ')';
}

// ===============================================================================================

function getHost()
{
   return window.location.host ? 'http://' + window.location.host : 'file://';
}

function getPath(windowObj)
{
   windowObj = windowObj || window;
   var pName = windowObj.location.pathname.toLowerCase();
   var path  = pName.match(/superm\//) ? pName.substring(pName.lastIndexOf('superm/') + 7, pName.lastIndexOf('/') + 1) : pName.substring(1, pName.lastIndexOf('/') + 1);
   return path;
}

   // figger out '../' prefix
function getDirLevel()
{
   var path = getPath();
   var prefix = '';
   for (var i = 0; i < path.length; i++)
      if (path.charAt(i) == '/')
         prefix += '../';
   return prefix;
}

function getMain(file)
{
   var path  = top.main ? top.main.location.pathname : window.location.pathname;
   var fName = path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf('.'));
   return file ? (file == fName) : fName;
}

   // update window title and location bar in framed site
function update()
{
   var path = top.main.location.pathname;
   var hash = top.main.location.hash;

// update our own location bar
   if (top.header && path.match(/blank/))
      top.header.document.getElementById('location').appendChild(document.createTextNode(getHost() + path + hash));

// reflect file in FRAMES.main in the tree
   if (top.treeFrame.toggleFileIcon)
      top.treeFrame.toggleFileIcon(getMain());

// update browser window title
   document.title = top.main.document.title;
}

   // load INTRO in FRAMES.main if it's not there already
function checkMain()
{
   getMain('intro') ? alert('You\'re HERE!') : (top.main.location = getDirLevel() + 'intro.html');
}

function viewSource(page)
{
   location = 'view-source:' + getHost() + page;
}

function goBack()
{
   if (!window.history.length)
   {
      alert('You\'re looking at it...');
      return;
   }
   window.history.back();
   window.focus();
}

   // return 'doc last modified' date in 'August 3d, 1999' format
function formatDate()
{
// this is more robust than parsing the 'document.lastModified' string which differs between browser releases and makes
   var modStr = new Date(Date.parse(window.document.lastModified));

// translate month
   var month;
   switch (modStr.getMonth())
   {
      case 0:  month = 'January';   break;
      case 1:  month = 'February';  break;
      case 2:  month = 'March';     break;
      case 3:  month = 'April';     break;
      case 4:  month = 'May';       break;
      case 5:  month = 'June';      break;
      case 6:  month = 'July';      break;
      case 7:  month = 'August';    break;
      case 8:  month = 'September'; break;
      case 9:  month = 'October';   break;
      case 10: month = 'November';  break;
      case 11: month = 'December';
   }

   var suffix = 'th';
   var test = modStr.getDate().toString();
// handle '1st', '21st', '31st', '2nd', '22nd', '3d', '23d'
   if (test.length == 1 || (test.length == 2 && test.substring(0, 1) != 1))
   {
      switch (test.substring(test.length - 1))
      {
         case '1': var suffix = 'st'; break;
         case '2': var suffix = 'nd'; break;
         case '3': var suffix = 'd';
      }
   }
   var ss = document.createElement('sup');
      ss.appendChild(document.createTextNode(suffix));
   var s = document.createElement('span');
      s.appendChild(document.createTextNode(month + ' ' + modStr.getDate()));
      s.appendChild(ss);
      s.appendChild(document.createTextNode(', ' + modStr.getFullYear()));
   return s;
}

   // open a confirm box to focus/display the tree window or redisplay the sitemap
function treeAlert(e)
{
   if (getCookie('showTreeAlert') == 1)
      fillModal('treeAlert', e);  // tree alert box is NOT disabled
   else if (top.treeWindow)
      top.treeWindow.focus();
   else
   {
      top.treeWindow        = window.open(getDirLevel() + 'frameset/tree.html', 'treeWindow', 'scrollbars,screenX=' + (window.screenX + getOuterWidth(window) - 200) + ',screenY=' + window.screenY + ',width=200,height=' + getOuterHeight(window));
      top.treeWindow.opener = window;
      top.treeWindow.focus();
   }
}

   // open a confirm box to restore frames or redisplay the introduction
function reframeAlert(e)
{
// inhibit getting framed siteMap
   if (getMain('lynx-idx'))
   {
      location = getDirLevel() + 'intro.html';
      return;
   }
   if (getCookie('showFramesAlert') == 1)
      fillModal('framesAlert', e);   // frame alert box is NOT disabled
   else
      top.location = getDirLevel() + ((getCookie('frameURL')) ? 'frameset/frames.html' : 'intro.html');
}

function unframe()
{
   var treeWidth = 300;
   document.location     = top.main.location;
   top.treeWindow        = window.open('./tree.html', 'treeWindow', 'screenX=' + (window.screenX + getOuterWidth(window) - treeWidth) + ',screenY=' + window.screenY + ',width=' + treeWidth + ',height=' + getOuterHeight(window));
   top.treeWindow.opener = window;
   top.treeWindow.focus();
}

function handleFrameAlert(choice)
{
// disable alert window
   if (parent.document.show.alert.checked)
      setCookie('showFramesAlert', 0, null, '/');

   if (choice)
   {
      if (top.treeWindow)
         top.treeWindow.close();

      var pName = parent.document.location.pathname.toLowerCase();
      var path  = (pName.match(/superm\//)) ? ('/' + pName.substring(1)) : ('../' + pName.substring(pName.lastIndexOf('superm/') + 7));
      setCookie('frameURL', path, null, '/');
      top.location = '../frameset/frames.html';
   }
   else
      top.location = '../intro.html';
}

   if (BrowserDetect.browser == 'explorer' && BrowserDetect.version <= 6)
   {
      if (!getCookie('warnedForErrors'))
      {
         setCookie('warnedForErrors', '*', '', '/');
         window.onerror = function() { document.location = getDirLevel() + 'errorPage.html'; };
      }
      else
         window.onerror = function() { alert('Don\'t come complaining about script errors; you have been advised to switch off scripting...'); };
   }

// hide sitemap icon in framed site or set sitemap icon onclick handler
   addLoadEvent(
      function()
      {
         cleanWhitespace(document.getElementById('footerIcons'));
         if (top.frames.length && top.treeFrame && document.getElementById('footer'))
         {
            document.getElementById('footerIcons').lastChild.style.display = 'none';
            document.getElementById('footerIcons').style.width             = '4em';    // center remaining icon
         }
         else if (document.getElementById('footerIcons') != null)       // the tree has no footer
         {
            var treeOpener = document.getElementById('footerIcons').lastChild;
         /* xPloder 7 has a known bug where it refuses to ack an onclick handler on a direct descendant of an A element,
          * only descendants at least once removed work as expected; the solution is to wrap the clickable element in a span
          */
            if (BrowserDetect.browser == 'explorer')
            {
               var clickParent = document.getElementById('footerIcons').lastChild;
               var clickNode   = clickParent.removeChild(clickParent.firstChild);
               var clickFixer  = document.createElement('span');
                  clickFixer.appendChild(clickNode);
               clickParent.appendChild(clickFixer);
               treeOpener = clickParent.firstChild.firstChild;
            }
            treeOpener.onclick =
            function(event)
            {
               event = event || getXploderEvent();

            // w3c [moz] says 0 = left, all others say 1 = left; see http://www.quirksmode.org/js/events_properties.html for details
               if ((event.button == 0 || event.button == 1) && (event.shiftKey && event.metaKey) || (event.shiftKey && event.ctrlKey))
               {
                  if (top.treeWindow)
                     return false;

                  var treeXPos   = BrowserDetect.browser == 'explorer' ?    0 : 1113;
                  var treeYPos   = BrowserDetect.browser == 'explorer' ?    0 :  560;
                  var treeWidth  = BrowserDetect.browser == 'explorer' ?  565 :  400;
                  var treeHeight = BrowserDetect.browser == 'explorer' ? 1200 :  835;

                  top.treeWindow          = window.open(getDirLevel() + 'frameset/tree.html', 'treeWindow', 'scrollbars,screenX=' + treeXPos + ',screenY=' + treeYPos + ',width=' + treeWidth + ',height=' + treeHeight);
                  top.treeWindow.opener   = self;
                  top.treeWindow.onunload = function() { top.treeWindow = null; top.treeWindow.opener = null; };
                  top.treeWindow.focus();

                  sendToDevNull(event);
               }
               else
               // restore original click handler
                  window.location.href = getDirLevel() + 'lynx-idx.html';
            };
         }
      }
   );
/*
// CSS tooltip debugging function
addLoadEvent(
function()
{
   var divs = document.getElementsByTagName('div');
   for (var i = 0; i < divs.length; i++)
   {
      if (divs[i].className == 'abs tipArea')
      {
         cleanWhitespace(divs[i]);

         if (getCookie('currentID') && getCookie('currentID') == divs[i].id)
            divs[i].firstChild.style.display = 'block';

         divs[i].onclick =
         function(event)
         {
            if (event.target.className == 'rel tipContent')
            {
               if (this.firstChild.firstChild.childNodes.length > 1)
                  this.firstChild.firstChild.removeChild(this.firstChild.firstChild.lastChild);
               this.firstChild.style.display = '';
               return;
            }

            if (this.firstChild.style.display == 'block')
               return;

            this.firstChild.style.display = 'block';
            this.firstChild.firstChild.appendChild(document.createTextNode(' - ' + this.id));
            setCookie('currentID', this.id);
         };
      }
   }
}
);
*/
