/*****************************************************
# $Id: core.js,v 1.241 2009/09/21 20:50:15 pblankenbaker Exp $

 JavaScript file to add NST headers with next/prev links
*****************************************************/

//
// domTT Global Variables
//
// Default: Set class for domTT tooltips...
var domTT_styleClass = 'niceTitle';

// Newer versions of domLib (from domMenu package) renamed
// the "detect collisions" function, put in a wrapper to
// keep the domTT package happy.
function domLib_detectCollisions(in_object, in_recover, in_useCache) {
  domLib_detectObstructions(in_object, in_recover, in_useCache);
}

//
// NST WUI Global Variables...
//
// sectCnt - Used to keep track of section headers in addSectionLink()
// beginDate - Time page was loaded (as Date object)
// startTime - Time page was loaded (as integer millisecond count)
// sid - Session ID (set farther down in this file)
var sectCnt=1;
var beginDate = new Date();
var startTime = beginDate.getTime();

// Assume page can handle "smart page reloads" (like positionReload()).
var smartReload = true;

//
// Determines if Netscape/Mozilla/Firefox browser is being used.
function isNetscape() {

  return (navigator.appName == "Netscape");
}

//
// Determines if IE browser is being used...
function isIE() {

  return (navigator.appName == "Microsoft Internet Explorer");
}

//
// Determines if IE8 browser is being used...
function isIE8() {

  if (isIE()) {
    return (navigator.appVersion.indexOf('MSIE 8') != -1);
  } else {
    return false;
  }
}

//
// get browser agent info object and set to lower case...
  var _userAgent = navigator.userAgent.toLowerCase();

//
// Determines if browser running on Linux...
function isLinux() {
  return (_userAgent.indexOf('linux') != -1);
}

//
// Determines if browser running on iPod...
function isiPod() {
  return (_userAgent.indexOf('ipod') != -1);
}

//
// Determines if browser running on BlackBerry PDA...
function isBlackberry() {
  return (_userAgent.indexOf('blackberry') != -1);
}

//
// Determines if browser running on Mozilla Firefox...
function isFirefox() {
  return (_userAgent.indexOf('firefox') != -1);
}

//
// Determines if browser running on Google Chrome...
function isChrome() {
  return (_userAgent.indexOf('chrome') != -1);
}

//
// Returns true if we know that we are in quirks mode
function isInQuirksMode() {
  return (document.compatMode == 'BackCompat');
}


//
// Default: Postpone DOM tooltips until page is loaded
var domTT_postponeActivation = true;
//
// Permit "sneaky" tooltip enabling for load average when NOT using IE
// (IE has a "sometimes" error when DOM tooltips are enabled prior to
// the page being fully loaded).
var nst_domTT_loadHoverActivate = ! isIE();
//
// Check for user session override...
try {
  if (_SESSION['domTT_postponeActivation']) {
    if (_SESSION['domTT_postponeActivation'] == "false") {
      domTT_postponeActivation = false;
    }
  }
} catch (e) {
}

//
// Allow the DOM Tooltip delay to be overridden by user...
//
// Default: Initially set tooltip delay to: '1.000 secs'...
domTT_activateDelay = 1000;
//
// Set long initial tooltip delay (1 min) for the
// Apple iPod iPhone/iTouch device...
if (isiPod()) {
  domTT_activateDelay = 60000;
}
//
// Check for user session override...
try {
  if (_SESSION['domTT_activateDelay']) {
    domTT_activateDelay = _SESSION['domTT_activateDelay'];
  }
} catch (e) {
}


/* Static class of helper function to build DOM parts.
 *
 * Refer to the methods defined below on what one can do with the NstDom
 * object. */

var NstDom = new function() {
  // Flag used to tell if we've been initialized or not
  this._Initialized = false;

  this._OrderedHeaders = [];
  this._HeaderMap = [];
  this._IdEnhancements = [];
  this._ExitUrl = false;
  this._ExitFunction = false;

  // Indicates that we have not yet computed the scroll bar width
  this._ScrollBarWidthComputed = false;

  // Associative array of tool tips
  this._ToolTips = new Array();
  this._ToolTipWidths = new Array();

  // Set of "onload" and "onunload" handlers
  this._OnloadHandlers = new Array();
  this._OnunloadHandlers = new Array();
};

/* NstDom.activate()
 *
 *   Currently, we require one to activate the NstDom singleton
 *   before any of "registered" features will be enabled.  This is due
 *   to the fact that this code is included by MANY different pages
 *   which may not be compatible with the NST framework.  NOTE: It
 *   does not hurt to invoke this method multiple times (all
 *   subsequent invocations are ignored).
 *
 *   NOTE: Initialization will take control of some of the window
 *   events (like "onload" and "onunload").
 */

NstDom.activate = function() {
  if (NstDom._Initialized) {
     return NstDom;
  }
  NstDom._Initialized = true;

  // Add onmouseover for registered tooltips
  NstDom.registerOnloadHandler(NstDom.idEnhance);
  NstDom.registerOnloadHandler(NstDom.headerEnhance);
  NstDom.registerOnloadHandler(NstDom.spanEnhance);

  // Guarantee scroll to 'hash' window.location for IE Browsers...
  if (isIE()) {
    NstDom.registerOnloadHandler(NstDom.scrollToHash);
  }

  window.onload = NstDom.runAndClearOnloadHandlers;
  window.onunload = NstDom.runAndClearOnunloadHandlers;

  return NstDom;
}

/* NstDom.doNothing()
 *
 *   Function which does nothing. Useful to disable existing functions. */

NstDom.doNothing = function() {
}

/* NstDom.disableSpanEnhance()
 *
 *   Disables the enhancment of spans which is enabled when
 *   one invokes "activate". */

NstDom.disableSpanEnhance = function() {
  NstDom.spanEnhance = NstDom.doNothing;
}

/* NstDom.disableHeaderEnhance()
 *
 *   Disables the enhancment of headers which is enabled when
 *   one invokes "activate". */

NstDom.disableHeaderEnhance = function() {
  NstDom.headerEnhance = NstDom.doNothing;
}

/* NstDom.addToolTip(id, DOM_TOOLTIP) 
*
*  Add a new tooltip to the collection.
*
*  id - ID of tooltip to add (nice if it matches ID of HTML element as well)
*  tooltip - String or full DOM entity to show as tooltip.
*  width - Optional width parameter. */

NstDom.addToolTip = function(id, tooltip, width) {
  NstDom._ToolTips[id] = tooltip;
  if (width) {
    NstDom._ToolTipWidths[id] = width;
  }
}

/* NstDom.getToolTip(id)
 *
 * Get tooltip associated with a particular ID which was previously added.
 *
 * id - ID of tooltip
 * 
 * return String or DOM node, or undefined. */

NstDom.getToolTip = function(id) {
    return NstDom._ToolTips[id];
}

/* NstDom.registerOnloadHandler(f)
 *
 * Registers a "onload" handler function "f" which will be invoked AFTER the
 * page is loaded. */

NstDom.registerOnloadHandler = function(f) {
  NstDom._OnloadHandlers.push(f);
}

/* NstDom.registerOnunloadHandler(f)
 *
 * Registers a "onunload" handler function "f" which will be invoked when the 
 * page is unloaded. */

NstDom.registerOnunloadHandler = function(f) {
  NstDom._OnunloadHandlers.push(f);
}

/*
 * ===== BEGIN PRIVATE NstDom METHODS - YOU SHOULD NOT USE! =====
 */

/* NstDom.runAndClearOnloadHandlers()
 *
 * Runs all of the registered "onload" handlers AFTER the page has
 * loaded. You do not call this method directly (it is invoked
 * automatically). */

NstDom.runAndClearOnloadHandlers = function() {

    for (var idx in NstDom._OnloadHandlers) {
	var handler = NstDom._OnloadHandlers[idx];
        //        try {
          handler();
        /*        } catch (e) {
          alert("Failed to run handler[" + idx + "]\n\n" + handler);
          } */
    }

    // Set any registered tooltips
    NstDom.registerToolTips();

    NstDom._OnloadHandlers = new Array();
}

/* NstDom.runAndClearOnunloadHandlers()
 *
 * Runs all of the registered "onload" handlers AFTER the page has
 * loaded. You do not call this method directly (it is invoked
 * automatically). */

NstDom.runAndClearOnunloadHandlers = function() {

    for (var idx in NstDom._OnunloadHandlers) {
	var handler = NstDom._OnunloadHandlers[idx];
	handler();
    }

    NstDom._OnunloadHandlers = new Array();
}

/* NstDom.showToolTip(node[, event[, ttid])
 *
 * Private method (you should not need to invoke this directly), which
 * handles the "onmouseover" event for elements affected by the 
 * NstDom.registerToolTips() invocation.
 *
 * node - DOM element to show tooltip for.
 *
 * event - Window event (or null if IE).
 *
 * ttid - Optional ID of tooltip (if omitted, we will use the
 * "_ToolTipId" attribute from the node if available or the nodes "id"
 * if not). */

NstDom.showToolTip = function(node, event, ttid) {

    if (event == null) {
	event = window.event;
    }

    // If tooltip id not specified, use ID of element
    if (ttid == null) {
      if (node._ToolTipId) {
        ttid = node._ToolTipId;
      } else {
	ttid = node.id;
      }
    }

    var tt = NstDom.getToolTip(ttid);
    if (tt) {
      var width = 300;
      if (NstDom._ToolTipWidths[ttid]) {
        width = NstDom._ToolTipWidths[ttid];
      } else {
        width = 300;
	if (tt.length > 30) {
	  width = 460;
	}
      }
      domTT_activate(node, event, 'content', tt, 'width', width);
    } else if (ttid != "disable") {
      domTT_activate(node, event, 'content', 
                     'No tooltip registered with ID: '
                     + NstDom.ttValue(ttid, true), 'width', 320);
    }

}

/* NstDom.registerToolTips()
 *
 * Private method (you should not need to invoke this directly), which
 * adds tool tip handlers to all of the elements on the page which have a "id"
 * attribute which matches one of the "id"s that was previously registered
 * via the "addToolTip" function. NOTE: Does NOT replace onmouseover handler
 * if one was already installed for the element. */

NstDom.registerToolTips = function() {

    for (var id in NstDom._ToolTips) {
	var node = document.getElementById(id);
	if (node && !node.onmouseover) {
	    
	    node.onmouseover = function(e) {
		NstDom.showToolTip(this, e);
	    }
	}
    }

    // Enable DOM tooltips now
    domTT_documentLoaded = true;

}

/*
 * ===== END PRIVATE NstDom METHODS - YOU SHOULD NOT USE! =====
 */

/* NstDom.showExitToolTip(node, event, label, title)
 *
 * Show DOM tooltip for exit button.
 *
 * node - The node (button).
 * event - The event triggering the need to show.
 * label - The label of the button (or '' if unknown).
 * title - The title to appear (or '' if unknown). */

NstDom.showExitToolTip = function(node, event, label, title) {

  // Choose some default (if omitted)
  if (label == '') {
    label = 'Exit';
  }
  if (title == '') {
    title = 'Calling Page';
  }

  var tooltip = NstDom.ttAction(label) + " to page: "
    + NstDom.ttEmphasis(title, true) + "...";

  //
  // 15 extra chars + length label and title minus an offset...
  var width = calcDomTTLen(15 + label.length + title.length, -40);

  domTT_activate(node, event, 'content', tooltip, 'width', width);

}

/* NstDom.getSubmitValue(key, def)
 *
 * If the JavaScript _SUBMIT[] array exists and contains a entry
 * for the specified 'key', the value associated with the entry
 * will be returned. Otherwise the default value is returned.
 *
 * NOTE: Typially CGI is used to build the _SUBMIT[] array.
 *
 * key - The 'key' name to use in lookup.
 * def - The default value to return if nothing is assocated with 'key'. */

NstDom.getSubmitValue = function(key, def) {
  if (window._SUBMIT && (typeof _SUBMIT[key] != typeof undefined)) {
    return _SUBMIT[key];
  }
  return def;
}

/* NstDom.showExitToolTipAdvanced(event)
 *
 *  */

  NstDom.showExitToolTipAdvanced = function(node, event) {
  var tt = NstDom.getSubmitValue('return_tooltip', false);
  // Check for any POST/GET overrides
  if (tt) {
    var width = NstDom.getSubmitValue('return_tooltip_width', 300);
    
    domTT_activate(node, event, 'content', tt, 'width', width);
  } else {
    var label = NstDom.getSubmitValue('return_label', '');
    var title = NstDom.getSubmitValue('return_from', '');
    NstDom.showExitToolTip(node, event, label, title);
  }
}

/* NstDom.performExitAction()
 *
 * What to do when a "Exit/Return" button is pressed. */

NstDom.performExitAction = function(event) {
  var url = NstDom.getSubmitValue("return", NstDom._ExitUrl);
  if (NstDom._ExitFunction) {
    NstDom._ExitFunction();
  } else if (url) {
    window.location = url;
  } else {
    alert("Error in page - no Exit handler/URL set.");
  }
}

/* Builds a DOM node containing a single Exit/Return button.
 *
 * The resulting button behavior/appearance is controlled by:
 *
 *   NstDom.setExitUrl()
 *   NstDom.setExitFunction()
 *
 * If the _SUBMIT[] array is present, any of the following
 * entries will override the default behavior:
 *
 *   _SUBMIT['return']
 *     Exit/return URL to jump to on button press.
 *
 *   _SUBMIT['return_label']
 *     Label to appear on the button.
 *
 *   _SUBMIT['return_tooltip']
 *     Full DOM tooltip to use for button.
 *
 *   _SUBMIT['return_tooltip_width']
 *     Width for full tooltip (only used if 'return_tooltip' set).
 *
 *   _SUBMIT['return_from']
 *     Name of page to return to (only used when building
 *     tooltip when _SUBMIT['return_tooltip'] is omitted.
 */

NstDom.createExitButton = function() {
  // Create/initialize the new button
  var ebutton = document.createElement("button");
  ebutton.className = "wuiInputButton";
  ebutton.onmouseover = function(event) {
    NstDom.showExitToolTipAdvanced(this, event);
  }
  ebutton.onclick = NstDom.performExitAction;

  // Determine and set the label
  var label = "Exit";
  if (window._SUBMIT) {
    if (_SUBMIT["return_label"]) {
      label = _SUBMIT["return_label"];
    } else if (_SUBMIT["return"]) {
      label = "Return";
    }
  }
  ebutton.appendChild(document.createTextNode(label));

  return ebutton;
}

/* NstDom.replaceWithExitButton(node)
 *
 * 
 */

NstDom.replaceWithExitButton = function(node) {
  // If we have a return location, use it
  if (NstDom.getSubmitValue("return", false) || NstDom._ExitUrl) {
    var ebutton = NstDom.createExitButton();
    node.parentNode.replaceChild(ebutton, node);
  } else {
    node.parentNode.removeChild(node);
  }
}

/* NstDom.replaceWithExitButtonRow(node)
 *
 * 
 */

NstDom.replaceWithExitButtonRow = function(node) {
  var cblock = document.createElement("center");
  cblock.id = node.id;

  // If we have a return or exit location, insert button
  if (NstDom.getSubmitValue("return", false) || NstDom._ExitUrl) {
    // Create a centered button
    cblock.className = "exitButtonRow";
    var ebutton = NstDom.createExitButton();
    cblock.appendChild(ebutton);
  } else {
    // Just update the 
    cblock.className = "exitButtonRowNoButton";
    cblock.appendChild(document.createTextNode("\u00a0"));
  }

  // Replace existing node
  node.parentNode.replaceChild(cblock, node);
}

/* NstDom.getComputedStyle(node, cssStyleName)
 *
 *   Attempts to determine the computed style for a particular DOM node.
 *
 * node - The DOM node to get style of.
 * cssStyleName - The name of the CSS style to look up (ex: "font-size").
 * def - What to return if not found.
 *
 * Returns style value (like: "2px") or empty string if unable to determine.
 *
 * Based on "Robert's Talk" code example found at: 
 *
 * http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
 */

NstDom.getComputedStyle = function(node, cssStyleName, def) {

  if (document.defaultView && document.defaultView.getComputedStyle) {
    var styleInfo = document.defaultView.getComputedStyle(node, "");
    var styleVal = styleInfo.getPropertyValue(cssStyleName);
    return (styleVal ? styleVal : def);
  }

  if (node.currentStyle){
    var strCssRule = cssStyleName.replace(/\-(\w)/g,
                                          function (strMatch, p1){
                                            return p1.toUpperCase();
                                          });
    return (node.currentStyle[strCssRule] ? node.currentStyle[strCssRule] : def);
  }
  
  return def;
}

/* Convert string ID, DOM node or null to a DOM node.
 *
 * node - ASCII ID of node, null or DOM node.
 *
 * returns DOM node or null. */

NstDom.getNode = function(node) {
  if (!node) {
    return null;
  }
  if (typeof node == "string") {
    return document.getElementById(node);
  }
  return node;
}


/* Scroll to the specified DOM node into view...
 *
 * node - DOM node (or its ASCII ID) to scroll to bottom of.
 *
 * topbottom - Parameter to the JavaScript function: "scrollIntoView"
 *             Set it to "true" or "false", indicating whether the top (true)
 *             of the element is scrolled to the top of the window or the
 *             bottom (false) of the element is scrolled to the
 *             bottom of the window. */

NstDom.scrollTo = function(node, topbottom) {

  //
  // Get the DOM node to scroll into view...
  node = NstDom.getNode(node);
  if (!node) {
    return;
  }

  //
  // Try to scroll desired node into view...
  node.scrollIntoView(topbottom);
}

/* Scroll to the bottom of a specified DOM node.
 *
 * node - DOM node (or its ASCII ID) to scroll to bottom of.
 *
 * firefoxCorrect - Size (in pixels) to adjust for decoration(s) (I
 * think this is bottom margin) at the bottom of the widget.
 *
 * reserve - Size (in pixels) to reserve at the bottom. */

NstDom.scrollToBottom = function(node, ffc, reserve) {

  // Get the DOM node to scroll
  node = NstDom.getNode(node);
  if (!node) {
    return;
  }

  // Get view port dimensions
  var vsize = NstDom.getViewportSize();

  // Start with enough to scroll top of node to top of viewport
  var scrollAmount = node.offsetTop;
  // Move down enough to put bottom edge of node just off screen
  scrollAmount += node.offsetHeight;
  // Move up the size of the viewport
  scrollAmount -= vsize[1];

  // Firefox treats decorations differently than IE when scrolling
  if (!isIE()) {
    scrollAmount += ffc;
  }

  // Move back down enough for any reserved space at the bottom
  scrollAmount += (reserve ? reserve : 0);
  
  window.scrollTo(0, scrollAmount);
}


/* Scroll to the current location set by the 'hash' window.location
 * property.
 *
 * top - Pass true (or omit) to goto top, false to goto bottom.
 *
 * **Note: The scroll will only take place if there is a
 *         matching 'node id' on the page that has the
 *         same name as the 'hash' value. */

NstDom.scrollToHash = function(top) {
  // Force top to true if not explicitly set to true
  top = (top != false);

  NstDom.scrollTo(window.location.hash.replace(/#/,''), top);
}


/* Select a range of text within a DOM node. The text will be
 * highlighted...
 * 
 * node  - DOM node (or its ASCII ID). This is
 *         typically an text input field.
 *
 * start - Position of start of text (numerical value).
 * end   - Position of end of text (numerical value).
 *
 * Returns: Returns "true" if successful otherwise "false". */

NstDom.setSelectionRange = function(node, start, end) {

  //
  // Get the DOM node for range selection...
  node = NstDom.getNode(node);
  if (!node) {
    return false;
  }

  //
  // first set cursor insertion point node focus...
  node.focus();

  //
  // typically a gecko browser (i.e. firefox)...
  if (node.setSelectionRange) {
    node.setSelectionRange(start, end);
    return true;

  //
  // typically the IE browser...
  } else if (node.createTextRange) {
    var range = node.createTextRange();
    range.collapse(true);
    range.moveStart("character", start);
    range.moveEnd("character", end - start);
    range.select();
    return true;
  } else {
    return false;
  }
}


/* Get the start position of the selected (highlighted) text
 * within a DOM node.
 *
 * node  - DOM node (or its ASCII ID). This is
 *         typically an text input field.
 *
 * Returns: The index position of the start of the selection
 *          range. Returns "-1" if an invalid node is passed.
 *          Returns "-2" if no method is found for determining
 *          the start position. */

NstDom.getSelectionStart = function(node) {

  //
  // Get the DOM node for start of selection value...
  node = NstDom.getNode(node);
  if (!node) {
    return -1;
  }

  //
  // typically a gecko browser (i.e. firefox)...
  if (node.selectionStart) {
    return node.selectionStart;

  //
  // typically the IE browser...
  } else if (node.createTextRange) {
    var range = node.createTextRange();
    var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
    if (! isCollapsed) {

  //
  // move insertion point to beginning of text...
      range.collapse(true);
    }
    var b = range.getBookmark();
    return b.charCodeAt(2) - 2;
  } else {
    return -2;
  }
}


/* Get the end position of the selected (highlighted) text
 * within a DOM node.
 *
 * node  - DOM node (or its ASCII ID). This is
 *         typically an text input field.
 *
 * Returns: The index position of the end of the selection
 *          range. Returns "-1" if an invalid node is passed.
 *          Returns "-2" if no method is found for determining
 *          the end position. */

NstDom.getSelectionEnd = function(node) {

  //
  // Get the DOM node for end of selection value...
  node = NstDom.getNode(node);
  if (!node) {
    return -1;
  }

  //
  // typically a gecko browser (i.e. firefox)...
  if (node.selectionEnd) {
    return node.selectionEnd;

  //
  // typically the IE browser...
  } else if (node.createTextRange) {
  var range = node.createTextRange();
  var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
  if (! isCollapsed) {

  //
  // move insertion point to end of text...
    range.collapse(false);
  }
	var b = range.getBookmark();
	return b.charCodeAt(2) - 2;
  } else {
    return -2;
  }
}


/* Set the cursor insertion point focus within a text field
 * at the "end" of the text. IE puts the cursor focus at the
 * begining of the text field while Firefox puts it at the end.
 *
 * node  - DOM node (or its ASCII ID). This is
 *         typically an text input field.
 *
 * Returns: Returns "true" if successful otherwise "false". */

NstDom.setCursorFocusEnd = function(node) {

  //
  // Get the DOM node...
  node = NstDom.getNode(node);
  if (!node) {
    return false;
  }

  //
  // if not the IE browser...
  if (! isIE()) {
    node.focus();
    return true;
  } else {
  //
  // get the end position of text within the field...
    var endpos = NstDom.getSelectionEnd(node);
    if (endpos >= 0) {
      var rs = NstDom.setSelectionRange(node, endpos, endpos);
      return rs;
    } else {
      return false;
    }
  }
}


/* Set the cursor insertion point focus within a text field
 * at the "start" of the text.
 *
 * node  - DOM node (or its ASCII ID). This is
 *         typically an textarea field.
 *
 * Returns: Returns "true" if successful otherwise "false". */

NstDom.setCursorFocusStart = function(node) {

  //
  // Get the DOM node...
  node = NstDom.getNode(node);
  if (!node) {
    return false;
  }

  // 
  // Set node to have focus
  node.focus();

  //
  // Firefox requires us to set position to start
  if (isNetscape()) {
    NstDom.setSelectionRange(node, 0, 0);
  }

  return true;
}



/* Returns current vertical scroll offset (or -1 if unable to determine). */

NstDom.getScrollY = function() {
  if (document.body.scrollTop) {
    return document.body.scrollTop;
  }  
  if (document.documentElement && document.documentElement.scrollTop) {
    return document.documentElement.scrollTop;
  }
  // Don't know how to get at this point, OR all above returned 0
  return 0;
}

/* Returns current horizontal scroll offset. */

NstDom.getScrollX = function() {
  if (document.body.scrollLeft) {
    return document.body.scrollLeft;
  }  
  if (document.documentElement && document.documentElement.scrollLeft) {
    return document.documentElement.scrollLeft;
  }
  // Don't know how to get at this point, OR all above returned 0
  return 0;
}

/* NstDom.getScrollBarWidth()
 *
 * Returns the width of the vertical scroll bar in pixels (if possible). */

NstDom.getScrollBarWidth = function() {
  // Only compute one time

/*

  if (this._ScrollBarWidthComputed) {
    if (NstDom.debugLevel(8, true)) {
      NstConsole.echo("Estimated Scrollbar Width: " + this._ScrollBarWidth);
    }
    return this._ScrollBarWidth;
  }
  this._ScrollBarWidthComputed = true;
*/

  /*
   * Paul's method (2008-04-25):
   *
   * <table>
   *  <tbody>
   *   <tr>
   *     <td> // block with fixed width height
   *      <div> // div with overflow set to scroll
   *       <div> // inner div
   *      
   * - Display everything off the visible area
   * - Scroll bar width is width of outer most <td> minus
   *   width of innermost div. */

  // Create table and position off page
  var table = document.createElement('table');
  table.style.position = 'absolute';
  table.style.left = '-1000px';
  table.style.top = '-1000px';

  var tbody = document.createElement('tbody');
  table.appendChild(tbody);

  var tr = document.createElement('tr');
  tbody.appendChild(tr);

  var td = document.createElement('td');
  tr.appendChild(td);
  td.style.margin = '0px';
  td.style.padding = '0px';
  td.style.border = 'none';

  var scroll = document.createElement('div');
  td.appendChild(scroll);
  scroll.style.margin = '0px';
  scroll.style.padding = '0px';
  scroll.style.border = 'none';
  scroll.style.overflow = 'scroll';

  var inner = document.createElement('div');
  scroll.appendChild(inner);
  inner.style.margin = '0px';
  inner.style.padding = '0px';
  inner.style.border = 'none';
  inner.style.height = "100px";
  inner.style.width = "100px";

  document.body.appendChild(table);

  // Get width of vertical scroll bar
  if (NstDom.debugLevel(9, true)) {
    NstConsole.echo("td Offset Width: " + td.offsetWidth);
    NstConsole.echo("inner Offset Width: " + inner.offsetWidth);
  }
  this._ScrollBarWidth = (td.offsetWidth - inner.offsetWidth);
  //
  // check for 0 scroll bar width - found Google Chrome (Beta)...
  if (this._ScrollBarWidth == 0) {
    this._ScrollBarWidth = 17;
  }

  // Get height of horizontal scroll bar
  if (NstDom.debugLevel(9, true)) {
    NstConsole.echo("td Offset Height: " + td.offsetHeight);
    NstConsole.echo("inner Offset Height: " + inner.offsetHeight);
  }
  this._ScrollBarHeight = (td.offsetHeight - inner.offsetHeight);

  document.body.removeChild(table);

  return this._ScrollBarWidth;
}

/* NstDom.getScrollBarHeight()
 *
 * Returns the width of the vertical scroll bar in pixels (if possible). */

NstDom.getScrollBarHeight = function() {
  // The NstDom.getScrollBarWidth method does the actual measurement
  if (!this._ScrollBarWidthComputed) {
    NstDom.getScrollBarWidth();
  }
  return this._ScrollBarHeight;
}

/** Try to determine whether or not a horizontal scroll bar is displayed
 * for a particular node.
 *
 * node - DOM node to check.
 *
 * returns true if shown, false if not. */

NstDom.isHorizontalScrollBarShown = function(node) {
  var mode = NstDom.getComputedStyle(node, "overflow-x", "unknown");
  if (mode == "scroll") {
    return true;
  }
  if ((mode == "auto") && (node.offsetWidth < node.scrollWidth)) {
    return true;
  }
  return false;
}

/** Try to determine whether or not a vertical scroll bar is displayed
 * for a particular node.
 *
 * node - DOM node to check.
 *
 * returns true if shown, false if not. */

NstDom.isVerticalScrollBarShown = function(node) {
  var mode = NstDom.getComputedStyle(node, "overflow-y", "unknown");
  if (mode == "scroll") {
    return true;
  }
  if ((mode == "auto") && (node.offsetHeight < node.scrollHeight)) {
    return true;
  }
  return false;
}

/* NstDom.debugLevel(level, andVisible)
 *
 * This method will return true, IF AND ONLY IF:
 *
 * - The NstConsole is available (JavaScript loaded).
 * - The NstConsole is shown (if andVisible == true).
 * - The NstConsole debug level is at the level specifed.
 *
 * level - Debug level to test (for example, if you pass 4, then
 * true will only be returned if the NstConsole debug level is set
 * to 4 or higher).
 *
 * andVisible - If true, then false will ALWAYS be returned when
 * the NstConsole is not currently active. */

NstDom.debugLevel = function(level, andVisible) {

  if (window.NstConsole) {
    if (NstConsole.debugLevel(level)) {
      return ((andVisible == false) || NstConsole.isVisible());
    }
  }
  return false;

}

/* NstDom.getViewportSize()
 *
 * If able to determine the viewable area of the screen, this method
 * returns an array containing two elements [ width_pixels, height_pixels ].
 *
 * This code is a slightly modified version of what was presents at:
 * http://ecmascript.stchur.com/2006/09/.
 *
 * @return Array containing width and height (in pixels) - or null if unable
 * to determine. */

NstDom.getViewportSize = function() {
  var size = null; // Assume unable to determine

  // First try to get info from the 'window' object
  if (window.innerWidth) {
    size = [ window.innerWidth, window.innerHeight ];

  // Next, try getting info from 'document.documentElement'
  } else if (document.documentElement &&
	     document.documentElement.clientWidth) {
    size = [ document.documentElement.clientWidth,
	     document.documentElement.clientHeight ];
  } else {
    // Finally, fall back to checking clientWidth/clientHeight of <body>
    var body = document.getElementsByTagName('body')[0];
    if (body && body.clientWidth) {
      size = [ body.clientWidth, body.clientHeight ];
    }
  }

  if (size && NstDom.debugLevel(9, true)) {
    NstConsole.echo("Current viewport width: " + size[0] + "  height:" + size[1]);
  }

  return size;    
}


/* NstDom.setViewportHeight(node, fixedHeight, decorHeight)
 *
 *   Used to set the height of a DOM node such that it fits in
 *   nicely within the user's scroll area.
 *
 * node - DOM node to set the size of.
 * 
 * fixedHeight - Fixed amount to subtract off (in pixels). Should
 * be the size of any other vertically placed nodes which will appear.
 *
 * decorHeight - Height (in pixels) of any decoration spacing (margin,
 * border, padding) in the node's style. */

NstDom.setViewportHeight = function(node, fixedHeight, decorHeight) {
  var vsize = NstDom.getViewportSize();
  var h = vsize[1];
  if (window.ActiveXObject) {
    // Prior to new DOCTYPE
    // h -= fixedHeight;
    h -= (fixedHeight + decorHeight);
  } else {
    h -= (fixedHeight + decorHeight);
  }
  node.style.height = h + "px";

  if (NstDom.debugLevel(6, true)) {
    var id = "node";
    if (node.id) {
      id = node.id;
    }
    NstConsole.echo(id + " adjusted height: " + h + "px");
  }

}

/* NstDom.setViewportWidth(node, fixedWidth, decorWidth)
 *
 *   Used to set the width of a DOM node such that it fits in
 *   nicely within the user's scroll area.
 *
 * node - DOM node to set the size of.
 * 
 * fixedWidth - Fixed amount to subtract off (in pixels). Should
 * be the size of any other horizontally placed nodes which will appear.
 *
 * decorWidth - Width (in pixels) of any decoration spacing (margin,
 * border, padding) in the node's style. */

NstDom.setViewportWidth = function(node, fixedWidth, decorWidth) {
  var vsize = NstDom.getViewportSize();
  var w = vsize[0];
  if (window.ActiveXObject) {
    // Prior to new DOCTYPE
    // w -= (fixedWidth);
    w -= (fixedWidth + decorWidth);
  } else {
    // Firefox requires that we backoff the decoration and scroll bar width
    w -= (fixedWidth + decorWidth + NstDom.getScrollBarWidth());
  }
  node.style.width = w + "px";

  if (NstDom.debugLevel(6, true)) {
    var id = "node";
    if (node.id) {
      id = node.id;
    }
    NstConsole.echo(id + " adjusted width: " + w + "px");
  }
}

/* NstDom.setExitUrl(url)
 *
 *   Set URL to associate with the "exit" location (enables "EXIT" icon on 
 *   header bars). This is only used if a exit function was not set. */

NstDom.setExitUrl = function(url) {
  this._ExitUrl = url;
}

/* NstDom.setExitFunction(url)
 *
 *   Set function to associate with the "exit" action (enables "EXIT" icon on 
 *   header bars). This overrides any exit URL which was set. */

NstDom.setExitFunction = function(f) {
  this._ExitFunction = f;
}


/* NstDom.getTextContent(node)
 *
 *   Verifies that DOM node contains a single #TEXT entity and returns
 *   the text of the #TEXT entity as a string (or null if not found). */

NstDom.getTextContent = function(node) {
    if (node && (node.childNodes.length == 1) && (node.firstChild.data)) {
	return node.firstChild.data;
    }
    return null;
}

/* NstDom.registerSection(id)
 *
 * id - ID of header (must not be null). */

NstDom.registerSection = function(id) {
  NstDom._HeaderMap[id] = NstDom._OrderedHeaders.length;
  NstDom._OrderedHeaders.push(id);
}

/* NstDom.ttAcronym(acronym) - Tooltip format a acronym. */

NstDom.ttAcronym = function(acronym) {
  return "<span class=\"ttAcronym\">" + acronym + "</span>";
}

/* NstDom.ttAction(action[, quote])
 *
 * Tooltip format a action (pass true as 2nd parameter to have it quoted). */

NstDom.ttAction = function(action, quote) {
  var tt = quote ? "'" : "";
  tt += "<span class=\"ttAction\">" + action + "</span>";
  if (quote) {
    tt += "'";
  }
  return tt;
}

/* NstDom.ttCommand(command[, quote])
 *
 * Tooltip format a command (pass true as 2nd parameter to have it quoted). */

NstDom.ttCommand = function(command, after_colon) {
  var tt = after_colon ? "'" : "";
  tt += '<span class="ttCommand">' + action + '</span>';
  if (after_colon) {
    tt += "'";
  }
  return tt;
}

/* NstDom.ttEmphasis(text[, quote])
 *
 * Tooltip format for emphasis (pass true as 2nd parameter to have it quoted). */

NstDom.ttEmphasis = function(text, sq) {
  var tt = sq ? "'" : "";
  tt += '<span class="ttEmphasis">' + text + '</span>';
  if (sq) {
    tt += "'";
  }
  return tt;
}

/* NstDom.ttNav(page)
 *
 * Tooltip format a reference to another page or section in WUI. */

NstDom.ttNav = function(page) {
  return "'<span class=\"ttNav\">" + page + "</span>'";
}

/* NstDom.ttNote(note[, quote][, underline])
 *
 * Tooltip format a note (pass true as 2nd parameter to have it quoted). */

NstDom.ttNote = function(text, sq, ul) {
  var tt = sq ? "'" : "";
  if (ul) {
    tt += '<u><span class="ttNote">' + text + '</span></u>';
  } else {
    tt += '<span class="ttNote">' + text + '</span>';
  }
  if (sq) {
    tt += "'";
  }
  return tt;
}

/* NstDom.ttNormal(text[, quote])
 *
 * Tooltip format for normal text (pass true as 2nd parameter to have
 * it quoted). */

NstDom.ttNormal = function(text, sq) {
  var tt = sq ? "'" : "";
  tt += '<span class="ttNormal">' + text + '</span>';
  if (sq) {
    tt += "'";
  }
  return tt;
}

/* NstDom.ttNST()
 *
 * Tooltip format of NST acronym. */

NstDom.ttNST = function() {
  return NstDom.ttAcronym("NST");
}

/* NstDom.ttSite(site_or_url)
 *
 * Tooltip format a web site name or URL. */

NstDom.ttSite = function(site_or_url) {
  return '"<span class="ttSite">' + site_or_url + '</span>"';
}

/* NstDom.ttValue(value[, quote])
 *
 * Tooltip format a value (pass true as 2nd parameter to have it quoted). */

NstDom.ttValue = function(value, quote) {
  var tt = (quote ? '"' : "");
  tt += '<span class="ttValue">' + value + '</span>';
  if (quote) {
      tt += '"';
  }
  return tt;
}

/* NstDom.reloadSection(node)
 *
 * Searches for parent node whose ID was previously registered via
 * NstDom.registerSection(). Then attempts to reload the page and position
 * it at the specified position. */

NstDom.reloadSection = function(node) {
  var hidx = NstDom.getSectionNode(node);

  if ((hidx === undefined) || (hidx < 0)) {
    positionReload("#top");
  } else {
    // Move to location
    positionReload("#" + NstDom._OrderedHeaders[hidx]);
  }

}

/* NstDom.getSectionNode(node) 
 * 
 *   Searches for registered section node starting from 'node'. Checks
 *   'node' and all parent nodes until a node with a ID matching one
 *   of the registered sections is found.
 * 
 * node - The DOM node to start from (or ASCII string of ID of DOM
 * node to start from, or omit if 'this' works as the DOM node). */

NstDom.getSectionNode = function(node) {
  // If passed string (id attribute), look up node
  if (typeof node == "string") {
    node = document.getElementById(node);
  }

  // If 'node' not known, try to fall back to 'this' (if it looks like
  // a DOM node)
  if (!node) {
    if (this.parentNode) {
      node = this;
    } else {
      return undefined;
    }
  }

  for (; node != document; node = node.parentNode) {
    var hidx = NstDom._HeaderMap[node.id];
    if (hidx != undefined) {
      return hidx;
    }
  }

  return undefined;
}

/* NstDom.moveToPreviousSection(node)
 *
 * Searches for parent node whose ID was previously registered via
 * NstDom.registerSection(). Then jumps to section which was registered
 * just prior (or top of document if none). */

NstDom.moveToPreviousSection = function(node) {
  var hidx = NstDom.getSectionNode(node);

  if ((hidx === undefined) || (hidx <= 0)) {
    window.location = "#top";
    return;
  }

  // Move to location
  window.location = "#" + NstDom._OrderedHeaders[hidx - 1];
}

/* NstDom.moveToNextSection(node)
 *
 * Searches for parent node whose ID was previously registered via
 * NstDom.registerSection(). Then jumps to section which was registered
 * just after (or bottom of document if none). */

NstDom.moveToNextSection = function(node) {
  var hidx = NstDom.getSectionNode(node);

  var lastIndex = NstDom._OrderedHeaders.length - 1;
  if ((hidx === undefined) || (hidx >= lastIndex)) {
    window.location = "#bottom";
    return;
  }

  // Move to location
  window.location = "#" + NstDom._OrderedHeaders[hidx + 1];
}

/* Create a <img> element that user can click on to toggle the open/close
 * state (hide/show) another element on the page. 
 *
 * nodeId - ID of other element to toggle the state of. NOTE: If the nodeId
 * does not exist, then this method will return null. */

NstDom.createOpenCloseIcon = function(nodeId) {
  var node = document.getElementById(nodeId);
  if (!node) {
      return null;
  }

  // Build image icon and append to <div>
  var img = document.createElement("img");
  img.className = "bodyIconLeft";
  img.style.width = "27px";
  img.style.height = "22px";
  img.OpenedSrc = "/nst/images/folder.open.gif";
  img.ClosedSrc = "/nst/images/folder.gif";
  if (node.style.display == 'none') {
    img.src = img.ClosedSrc;
  } else {
    img.src = img.OpenedSrc;
  }
  img.toggleId = nodeId;

  img.onclick = function() {
    var icon = this;
    var entity = document.getElementById(icon.toggleId);
    if (entity.style.display == 'none') {
	entity.style.display = 'inline';
	icon.src = img.OpenedSrc;
    } else {
	entity.style.display = 'none';
	icon.src = img.ClosedSrc;
    }
  }

  img.onmouseover = function(event) {
    var tt = "<span  class=\"ttAction\">Click</span> on icon to <span  class=\"ttEmphasis\">hide</span>/<span  class=\"ttEmphasis\">show</span> contents";
    domTT_activate(this, event, 'content', tt, 'width', 240);
  }

  return img;
}

/* Create a <img> element that user can click on to jump to the top of
 * the display. */

NstDom.createTopOfPageIcon = function() {

    // Build image icon and append to <div>
    var img = document.createElement("img");
    img.className = "bodyIconRight";
    img.style.width = "15px";
    img.style.height = "23px";
    img.src = "/nst/images/links_top_arrow.gif";
    img.onclick = function() {
	window.location = "#top";
    }

    img.onmouseover = function(event) {
      var tt = "<span  class=\"ttAction\">Go</span> To The \'<span class=\"ttNav\">Top</span>\' Of The Page";
      domTT_activate(this, event, 'content', tt, 'width', 200);
    }

    return img;
}


/* Create a <img> element that user can click on to jump to the bottom of
 * the display. */

NstDom.createBottomOfPageIcon = function() {

    // Build image icon and append to <div>
    var img = document.createElement("img");
    img.className = "bodyIconRight";
    img.style.width = "15px";
    img.style.height = "23px";
    img.src = "/nst/images/links_bottom_arrow.gif";
    img.onclick = function() {
	window.location = "#bottom";
    }

    img.onmouseover = function(event) {
      var tt = "<span  class=\"ttAction\">Go</span> To The \'<span class=\"ttNav\">Bottom</span>\' Of The Page";
      domTT_activate(this, event, 'content', tt, 'width', 220);
    }

    return img;
}

/* Create a <img> element that user can click on to jump to the previous section
 * the display. */

NstDom.createPrevSectIcon = function() {

    // Build image icon and append to <div>
    var img = document.createElement("img");
    img.className = "bodyIconRight";
    img.style.width = "15px";
    img.style.height = "20px";
    img.src = "/nst/images/links_up_arrow.gif";
    img.onclick = function() {
	NstDom.moveToPreviousSection(this);
    }

    img.onmouseover = function(event) {
      var tt = "<span  class=\"ttAction\">Go</span> To The \'<span class=\"ttNav\">Previous</span>\' Section";
      domTT_activate(this, event, 'content', tt, 'width', 200);
    }

    return img;
}

/* Create a <img> element that user can click on to jump to the next section
 * the display. */

NstDom.createNextSectIcon = function() {

    // Build image icon and append to <div>
    var img = document.createElement("img");
    img.className = "bodyIconRight";
    img.style.width = "15px";
    img.style.height = "20px";
    img.src = "/nst/images/links_down_arrow.gif";
    img.onclick = function() {
	NstDom.moveToNextSection(this);
    }

    img.onmouseover = function(event) {
      var tt = "<span  class=\"ttAction\">Go</span> To The \'<span class=\"ttNav\">Next</span>\' Section";
      domTT_activate(this, event, 'content', tt, 'width', 180);
    }

    return img;
}

/* Create a <img> element that user can click on to jump to the previous section
 * the display. */

NstDom.createHomeIcon = function() {

    // Build image icon and append to <div>
    var img = document.createElement("img");
    img.className = "bodyIconRight";
    img.style.width = "21px";
    img.style.height = "22px";
    img.src = "/nst/images/links_home.gif";

    img.onclick = function() {
      if (NstDom.isProbeBuild()) {
        window.location = "/nstwui/index.cgi";
      } else {
        window.location = "/nst/welcome.html";
      }
    }

    img.onmouseover = function(event) {
      var pname = "NST Welcome";
      if (NstDom.isProbeBuild()) {
         pname = "NST WUI Index";
      }
      var tt = NstDom.ttAction("Go",false) + " To The "
        + NstDom.ttNav(pname,true) + " Page";
      domTT_activate(this, event, 'content', tt, 'width', 220);
    }

    return img;
}

/* Create a <img> element that user can click on to jump to the previous section
 * the display. */

NstDom.createReloadIcon = function() {

    var img = document.createElement("img");
    img.className = "bodyIconRight";
    img.style.width = "23px";
    img.style.height = "22px";
    img.src = "/nst/images/links_reload.gif";
    img.onclick = function() {
	NstDom.reloadSection(this);
    }

    img.onmouseover = function(event) {
      var tt = "<span  class=\"ttAction\">Reload</span> The \'<span class=\"ttNav\">Current</span>\' Page At This <span  class=\"ttValue\">Position</span>";
      domTT_activate(this, event, 'content', tt, 'width', 280);
    }

    return img;
}

/* NstDom.createExitIcon()
 *
 * Create a <img> element that user can click on to jump to exit the current
 * page. */

NstDom.createExitIcon = function() {

    var img = document.createElement("img");
    img.className = "bodyIconRight";
    img.style.width = "31px";
    img.style.height = "23px";
    img.src = "/nst/images/links_exit.gif";
    img.onclick = function() {
	if (NstDom._ExitFunction) {
          Nst.Com._ExitFunction(this);
        } else if (NstDom._ExitUrl) {
	  window.location = NstDom._ExitUrl;
	} else {
          window.location = "/nst/index.cgi";
	}
    }

    img.onmouseover = function(event) {
      var tt = "<span  class=\"ttAction\">Exit</span> From This Page";
      domTT_activate(this, event, 'content', tt, 'width', 160);
    }

    return img;
}

/* NstDom.headerEnhanceBody(node)
 *
 *   Enhances the 'node' give as if it is a header node within the main body.
 *
 * node - The DOM node to enhance (must not be null and have the id 
 * attribute set). */

NstDom.headerEnhanceBody = function(node) {

  if (node && node.id && !node.HasBeenEnhanced) {
    // Mark node as "header" enhanced (prevent duplicate enhancements)
    node.HeaderEnhanced = true;

    var combined = document.createElement("div");
    combined.className = "fullWidth";

    var header = document.createElement("div");
    header.className = 'bodyHeaderLeft';

    // If there is an associated area with the header, add open/close icon
    var hideCloseNode = document.getElementById(node.id + "-area");
    if (hideCloseNode) {
	var img = NstDom.createOpenCloseIcon(node.id + "-area");
    //
    // Save access to icon node...
        node.icon = img;
	img.className = "bodyOpenCloseIconLeft";
	header.appendChild(img);
    } else {
      // Add a bit of indent if no leading image
      var indent = document.createElement("span");
      indent.style.width = ".2em";
      indent.appendChild(document.createTextNode("\u00a0"));
      header.appendChild(indent);
    }

    // Move original content into new div
    //    var origContent = document.createElement("div");
    //    origContent.className = 'bodyHeaderLeft';
    while (node.firstChild) {
	var removed = node.removeChild(node.firstChild);
	header.appendChild(removed);
    }
    //    header.appendChild(origContent);

    //    header.firstChild.style.paddingLeft = ".2em";

    combined.appendChild(header);

    var icons = document.createElement("div");
    icons.className = 'floatRight';

    if (NstDom._ExitUrl || NstDom._ExitFunction) {
      icons.appendChild(NstDom.createExitIcon());
    }
    icons.appendChild(NstDom.createHomeIcon());
    icons.appendChild(NstDom.createReloadIcon());
    icons.appendChild(NstDom.createTopOfPageIcon());
    icons.appendChild(NstDom.createBottomOfPageIcon());
    icons.appendChild(NstDom.createPrevSectIcon());
    var img = NstDom.createNextSectIcon()
    img.style.paddingRight = ".2em";
    icons.appendChild(img);
    combined.appendChild(icons);

    var clear = document.createElement("br");
    clear.className = "clear";
    combined.appendChild(clear);

    node.appendChild(combined);
  }
}

/** NstDome.showNstConsoleTooltip(event)
 *
 * Show DOM tooltip for JavaScript console. */

  NstDom.showNstConsoleTooltip = function(node, event) {
  var tt = "<div class=\"line1px\">"
        + "<span  class=\"ttAction\">Toggle</span> the &quot;<span  class=\"ttValue\">Display</span>&quot; of the "
        + "\'<span  class=\"ttEmphasis\">NST JavaScript Console</span>\' "
        + "at the <span  class=\"ttEmphasis\">Bottom</span> of the page.</div>"
        + "<br /><div class=\"line1px\""
        + " style=\"margin-bottom: 0.3em;\">"
        + "<span  class=\"ttAction\">About</span>:&nbsp;&nbsp;<span  class=\"ttEmphasis\">NST</span> "
        + "<u><span  class=\"ttEmphasis\">J</span></u>"
        + "<span  class=\"ttEmphasis\">avaScript</span> <u><span  class=\"ttEmphasis\">C</span></u>"
        + "<span  class=\"ttEmphasis\">onsole</span>&nbsp;&nbsp;v"
        + NstDom.ttValue(NstConsole.getVersion())
        + "</div>"
        + "An <span  class=\"ttAction\">Interactive</span> <span  class=\"ttValue\">Interface</span> "
        + "and <span  class=\"ttValue\">Library</span> of <span  class=\"ttEmphasis\">JavaScript</span> "
        + "<span  class=\"ttValue\">Objects</span>, <span  class=\"ttValue\">Functions</span>, "
        + "<span  class=\"ttValue\">Methods</span> and <span  class=\"ttValue\">Properties</span> "
        + "designed to <span  class=\"ttAction\">Help</span> <span  class=\"ttEmphasis\">Software Engineers</span> "
        + "<span  class=\"ttAction\">Diagnose</span> and <span  class=\"ttAction\">Develop</span> "
        + "\'<span  class=\"ttEmphasis\">JavaScript Code</span>\' "
        + "for <span  class=\"ttAction\">Building</span> &quot;<span  class=\"ttValue\">Dynamic Web</span>&quot; pages.";

      domTT_activate(node, event, 'content', tt, 'width', 520);
}

/* NstDom.isProbeBuild()
 *
 * Returns true if site is built to be installed on a NST probe (false
 * indicates build is for main NST web site). */

NstDom.isProbeBuild = function() {
  return false;
}

/* Create a <img> element that user can click on to toggle display
 * of console. */

NstDom.createConsoleIconBanner = function() {

    var img = document.createElement("img");
    img.className = "footerIcon";
    img.style.width = "19px";
    img.style.height = "22px";
    img.src = "/nst/images/toggle_console.gif";
    img.onclick = function() {
	// Deactivate tooltip prior to console close (otherwise we get a 
	// stuck tooltip on the page)
	domTT_deactivate(this.id);

	NstConsole.toggleConsole();
    }

    img.onmouseover = function(event) {
      NstDom.showNstConsoleTooltip(this, event);
    }

    return img;
}

/* Show the tooltip for the "enable side navigation" action.
 *
 * pname - Optiona name of the page to show. */

NstDom.showEnableFramesTooltip = function(node, event, pname) {
  if (!pname) {
    if (NstDom.isProbeBuild()) {
      pname = "NST WUI Index";
    } else {
      pname = "NST Welcome";
    }
  }

  var tt = NstDom.ttNote("Go",false) + " To The " 
    + NstDom.ttEmphasis(pname,true) + " Page - "
    + NstDom.ttValue("Enable Side Navigation",true)
    + ".<br /><br />"
    + NstDom.ttNote("Hover",false) + " to "
    + NstDom.ttEmphasis("Enable",true) + " the "
    + NstDom.ttValue("NST WUI Menu Bar") + ".";

  domTT_activate(node, event, 'content', tt, 'width', '390',
                 'offsetX', 40, 'offsetY', 50);
}

/* NstDom.createStartPageIconBanner()
 *
 * Create a <img> element that user can click on to jump to the
 * start page (bit NST logo. */

NstDom.createStartPageIconBanner = function() {
  var url = "/nst/welcome.html";
  var pname = "NST Welcome";
  if (NstDom.isProbeBuild()) {
    url = "/";
    pname = "NST Start";
  }

  // Create default, then tweak for banner area
  var img = document.createElement("img");
  img.src = "/nst/images/splasnst157x46.gif";
  img.style.borderStyle = "none";
  img.style.margin = "0px";
  img.style.padding = "0px";
  img.style.height = "46px";
  img.style.width = "157px";

  img.onmouseover = function(event) {
    var tt = NstDom.ttAction("Go") + " To The "
      + NstDom.ttNav(pname,true) + " Page";
    domTT_activate(this, event, 'content', tt, 'width', 220);
  }

  // Make a link so user can right click
  var a = document.createElement("a");
  a.appendChild(img);
  a.href = url;
  a.style.height = img.style.height;
  a.style.width = img.style.width;
  
  return a;
}

/* NstDom.createSourceForgeIconBanner()
 *
 * Create a <img> element that user can click on to jump to the
 * NST Source Forge page. */

NstDom.createSourceForgeIconBanner = function() {

  // Create default, then tweak for banner area
  var img = document.createElement("img");
  img.src = "/nst/images/sf-icon2.gif";
  img.style.borderStyle = "none";
  img.style.margin = "0px";
  img.style.padding = "0px";
  img.style.height = "37px";
  img.style.width = "125px";

  img.onmouseover = function(event) {
    var tt = NstDom.ttAction("Go") + " To The "
      + NstDom.ttNav("NST Project",true) + " Page";
    domTT_activate(this, event, 'content', tt, 'width', 220);
  }

  // Make a link so user can right click
  var a = document.createElement("a");
  a.appendChild(img);
  a.href = "http://sourceforge.net/projects/nst";
  a.style.height = img.style.height;
  a.style.width = img.style.width;
  
  return a;
}

/* NstDom.ttSortable(node, event, sortedBy)
 *
 *   Displays a tooltop for a table column header which can be clicked
 *   on to sort the contents of the table.
 *
 * node - The DOM node to the column header (a <th> entity).
 * event - The event which trigger the tooltip.
 * sortedBy - A label of what we will sort on ("IP Address"). */

NstDom.ttSortable = function(node, event, sortedBy) {
  var tt = NstDom.ttNote("Sort",false) + " The Table Rows By: " 
    + NstDom.ttEmphasis(sortedBy, true);

  domTT_activate(node, event, 'content', tt, 'width',
                 calcDomTTLen(sortedBy.length, 100, 300, 1600));

}

/* Create a <img> element that user can click on to enable side navigation.
 *
 * pname - Optional name of the page to show. */

NstDom.createEnableFramesIconBanner = function(pname) {
  var a = document.createElement("a");

  a.target = "_top";
  if (NstDom.isProbeBuild()) {
    a.href = "/nstwui/frame.cgi";
  } else {
    a.href = "/nst/frame.html";
  }

  a.className = "navLinkA";
  a.onmouseover = function(event) {
    document.getElementById('domMenu_nav').style.display = 'block';
    NstDom.showEnableFramesTooltip(this, event, pname);
  }

  var img = document.createElement("img");
  img.className = "footerIcon";
  img.style.width = "24px";
  img.style.height = "24px";
  img.src = "/nst/images/contents.gif";

  a.appendChild(img);
  return a;
}

/* Show the tooltip for the "disable side navigation" action. */

NstDom.showDisableFramesTooltip = function(node, event) {

  var tt = NstDom.ttNote("Reload", false) + " Contents Of The " 
    + NstDom.ttEmphasis("Current",true) + " Page - "
    + NstDom.ttValue("Disable Side Navigation",true)
    + ".<br /><br />"
    + NstDom.ttNote("Hover",false) + " for more than "
    + NstDom.ttValue("4 secs", true) + " to "
    + NstDom.ttEmphasis("Disable",true) + " the "
    + NstDom.ttValue("NST WUI Menu Bar") + ".";

  domTT_activate(node, event, 'content', tt, 'width', '430',
                 'offsetX', 40, 'offsetY', 10);
}

/* Helper function for time delayed menu bar removal... */

NstDom.disableFramesIconBanner = function() {
  document.getElementById('domMenu_nav').style.display = 'none';
}

/* Create a <img> element that user can click on to disable side navigation. */

var menuBarDisableTimeoutID;

NstDom.createDisableFramesIconBanner = function() {
  var a = document.createElement("a");
  a.target = "_top";
  a.href = window.location;

  a.className = "navLinkA";
  a.onmouseover = function(event) {
    NstDom.showDisableFramesTooltip(this, event);
    menuBarDisableTimeoutID = setTimeout("NstDom.disableFramesIconBanner()", 4000);
  }

  a.onmouseout = function(event) {
    domTT_mouseout(this, event);
    clearTimeout(menuBarDisableTimeoutID);
  }

  var img = document.createElement("img");
  img.className = "footerIcon";
  img.style.width = "24px";
  img.style.height = "24px";
  img.src = "/nst/images/noframes.gif";

  a.appendChild(img);
  return a;
}

/* Create a <img> element that user can click on to jump to the top of
 * the display (for display in banner). */

NstDom.createTopOfPageIconBanner = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createTopOfPageIcon();
  img.src = "/nst/images/links_footertop_arrow.gif";
  img.style.width = "17px";
  img.style.height = "23px";
  img.className = "footerIcon";

  return img;
}

/* Create a <img> element that user can click on to jump to the previous section
 * the display (suitable for display in a banner region). */

NstDom.createPrevSectIconBanner = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createPrevSectIcon();
  img.src = "/nst/images/links_footerprev_arrow.gif";
  img.style.width = "17px";
  img.style.height = "23px";
  img.className = "footerIcon";

  return img;

}

/* Create a <img> element that user can click on to view the source
 * code (on a banner area).
 *
 * e - The HTML entity to modify (typically a <a> or <img> object).
 *
 * fname - Full path to the file to view the source of (or full URL on
 * Internet).
 *
 * ttname - Option name of file to show in tooltip (if different than
 * fname).  */

NstDom.setSourceViewHandler = function(e, fname, ttname) {

    if (!ttname) {
      ttname = fname;
    }

    // Determine if we should use PHP viewer or generice tail viewer
    if (fname.substring(0,7) == "http://") {
      // If URL, then just go to the URL
      e.onclick = function() {
        window.open(fname);
      }
    } else if (fname.indexOf(".php") > 0) {
      e.onclick = function() {
 	window.open("/nstwui/php/system/ShowSource.php?filename="
                    + escape(fname));
      }
    } else {
      e.onclick = function() {
 	window.open("/nstwui/cgi-bin/system/tail.cgi?lines=all&toggletextmode=ASCII&filename="
                    + escape(fname));
      }
    }
    e.onmouseover = function(event) {
      var tt = "<div class=\"line1px\">" + NstDom.ttNote("View") + " The " 
        + NstDom.ttEmphasis("Source Code",true) + "</div>"
        + "<table>"
        + "<tr><th align=\"left\">Source File"
        + NstDom.ttNormal(":", false) + "</th>"
        + "<td align=\"left\">" + NstDom.ttValue(ttname, true) 
        + "</td></tr></table>";
      domTT_activate(this, event, 'content', tt, 'width',
                     calcDomTTLen(ttname.length, 100, 300, 1600));
    }
}

/* Create a <img> element that user can click on to view the source
 * code (on a banner area).
 *
 * fname - Full path to the file to view the source of (or full URL on
 * Internet).
 *
 * ttname - Option name of file to show in tooltip (if different than
 * fname).  */

NstDom.createSourceViewIconBanner = function(fname, ttname) {

    if (!ttname) {
      ttname = fname;
    }

    // Build image icon and append to <div>
    var img = document.createElement("img");
    img.className = "footerIcon";
    img.style.width = "19px";
    img.style.height = "22px";
    img.src = "/nst/images/links_viewpage.gif";

    NstDom.setSourceViewHandler(img, fname, ttname);
    return img;
}

/* Create a <img> element that user can click on to jump to the previous section
 * the display. */

NstDom.createHomeIconBanner = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createHomeIcon();
  img.src = "/nst/images/links_footerhome.gif";
  img.style.width = "20px";
  img.style.height = "23px";
  img.className = "footerIcon";

  return img;
}

/* Create a <img> element that user can click on to jump to the previous section
 * the display. */

NstDom.createReloadIconBanner = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createReloadIcon();
  img.src = "/nst/images/header_reload.gif";
  img.style.width = "23px";
  img.style.height = "22px";
  img.className = "footerIcon";

  return img;

}

/* NstDom.createHomeIconNote()
 *
 * Create a <img> element that user can click on to jump to the "home" page
 * (to be used in Note areas). */

NstDom.createHomeIconNote = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createHomeIcon();
  img.src = "/nst/images/links_home_note.gif";
  img.style.width = "21px";
  img.style.height = "22px";
  img.className = "noteIconRight";

  return img;

}

/* NstDom.createExitIconNote()
 *
 * Create a <img> element that user can click on to jump to exit the current
 * page (suitable for display in note area). */

NstDom.createExitIconNote = function() {

  var img = NstDom.createExitIcon();
  img.className = "noteIconRight";

  return img;

}

/* Create a <img> element that user can click on to toggle the open/close
 * state (hide/show) another element on the page (for note area). 
 *
 * nodeId - ID of other element to toggle the state of. NOTE: If the nodeId
 * does not exist, then this method will return null. */

NstDom.createOpenCloseIconNote = function(nodeId) {

  // Create default, then tweak for banner area
  var img = NstDom.createOpenCloseIcon(nodeId);

  return img;

}

/* NstDom.createReloadIconNote()
 *
 * Create a <img> element that user can click on to jump to refresh the
 * current page (to be used in Note areas). */

NstDom.createReloadIconNote = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createReloadIcon();
  img.src = "/nst/images/links_reload_note.gif";
  img.style.width = "23px";
  img.style.height = "22px";
  img.className = "noteIconRight";

  return img;

}

/* NstDom.createTopOfPageIconNote()
 *
 * Create a <img> element that user can click on to jump to the top of page
 * (to be used in Note areas). */

NstDom.createTopOfPageIconNote = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createTopOfPageIcon();
  img.src = "/nst/images/links_top_arrow_note.gif";
  img.style.width = "15px";
  img.style.height = "23px";
  img.className = "noteIconRight";

  return img;

}

/* NstDom.createBottomOfPageIconNote()
 *
 * Create a <img> element that user can click on to jump to the bottom of page
 * (to be used in Note areas). */

NstDom.createBottomOfPageIconNote = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createBottomOfPageIcon();
  img.src = "/nst/images/links_bottom_arrow_note.gif";
  img.style.width = "15px";
  img.style.height = "23px";
  img.className = "noteIconRight";

  return img;

}

/* NstDom.createPrevSectIconNote()
 *
 * Create a <img> element that user can click on to jump to the previous section
 * (to be used in Note areas). */

NstDom.createPrevSectIconNote = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createPrevSectIcon();
  img.src = "/nst/images/links_up_arrow_note.gif";
  img.style.width = "15px";
  img.style.height = "20px";
  img.className = "noteIconRight";

  return img;

}

/* NstDom.createNextSectIconNote()
 *
 * Create a <img> element that user can click on to jump to the next section
 * (to be used in Note areas). */

NstDom.createNextSectIconNote = function() {

  // Create default, then tweak for banner area
  var img = NstDom.createNextSectIcon();
  img.src = "/nst/images/links_down_arrow_note.gif";
  img.style.width = "15px";
  img.style.height = "20px";
  img.className = "noteIconRight";

  return img;

}

/* NstDom.headerEnhanceNote(node)
 *
 *   Enhances the 'node' give as if it is a header node within a Notes section.
 *
 * node - The DOM node to enhance (must not be null and have the id 
 * attribute set). */

NstDom.headerEnhanceNote = function(node) {

  if (node && node.id && !node.HasBeenEnhanced) {
    // Mark node as "header" enhanced (prevent duplicate enhancements)
    node.HeaderEnhanced = true;

    var combined = document.createElement("div");
    combined.className = "fullWidth";

    var header = document.createElement("div");
    header.className = 'noteHeaderLeft';

    // If there is an associated area with the header, add open/close icon
    var hideCloseNode = document.getElementById(node.id + "-area");
    if (hideCloseNode) {
	var img = NstDom.createOpenCloseIconNote(node.id + "-area");
	img.className = "noteOpenCloseIconLeft";
	header.appendChild(img);
    }

    while (node.firstChild) {
	var removed = node.removeChild(node.firstChild);
	header.appendChild(removed);
    }

    combined.appendChild(header);

    var icons = document.createElement("div");
    icons.className = 'floatRight';

    if (NstDom._ExitUrl || NstDom._ExitFunction) {
      icons.appendChild(NstDom.createExitIconNote());
    }
    icons.appendChild(NstDom.createHomeIconNote());
    icons.appendChild(NstDom.createReloadIconNote());
    icons.appendChild(NstDom.createTopOfPageIconNote());
    icons.appendChild(NstDom.createBottomOfPageIconNote());
    icons.appendChild(NstDom.createPrevSectIconNote());
    icons.appendChild(NstDom.createNextSectIconNote());
    combined.appendChild(icons);

    var clear = document.createElement("br");
    clear.className = "clear";
    combined.appendChild(clear);

    node.appendChild(combined);
  }
}

/* NstDom.headerEnhance()
 *
 *   Scans for NstDom.registeredSection(s) which have a "enhanceable" class
 *   type and enhances them. */

NstDom.headerEnhance = function() {

  for (var sidx in NstDom._OrderedHeaders) {
    var id = NstDom._OrderedHeaders[sidx];
    var node = document.getElementById(id);
    if (node) {
      if (node.className == "bodyHeader") {
        NstDom.headerEnhanceBody(node);
      } else if (node.className == "noteHeader") {
        NstDom.headerEnhanceNote(node);
      }
    }
  }
}

/* NstDom.addIdEnhance()
 *
 *   Registers function to be applied to a specific DOM node (by it's
 *   "id" attribute) AFTER the document is loaded.
 *
 * Example:
 * 
 *
 * id - The "id" of the entity to be enhanced.
 *
 * f - The JavaScript function to be invoked (it will be passed the DOM
 * node corresponding to the "id"). */

NstDom.addIdEnhance = function(id, f) {
  if (id) {
    NstDom._IdEnhancements[id] = f;
  }
}

/* NstDom.idEnhance()
 *
 *   Applies enhancement method to each node registered by the
 *   NstDom.addIdEnhance() method. */

NstDom.idEnhance = function() {

  for (var id in NstDom._IdEnhancements) {
    var node = document.getElementById(id);
    var f = NstDom._IdEnhancements[id];
    // If node found and function exists, then apply the function
    if (node && f) {
      f(node);
    }
  }

  // Clear the array of applied enhancements
  NstDom._IdEnhancements = [];

}

/* NstDom.showKernelSourceToolTip(node, event[, srcName])
 *
 *   Shows tool tip for node (assuming it's a link to a kernel source file).
 *
 * node - DOM node to show tool tip for.
 * event - Window event.
 * srcName - Optional source name (if omitted, we'll use text of child). */

NstDom.showKernelSourceToolTip = function(node, event, srcName) {
  if (!srcName) {
    srcName = NstDom.getTextContent(node);
  }

  // If given, or able to determine source name
  if (srcName) {
    // build tool tip contents
    var tt = "View the contents of the kernel source file: "
	+ NstDom.ttValue(srcName, true)
	+ " at " 
	+ NstDom.ttSite(node.href)
	+ ".";

    // and show it
    domTT_activate(node, event, 'content', tt, 'width', 460);
  }

}

/* NstDom.replaceKernelSource(node)
 *
 *   If DOM 'node' contains a single #TEXT entity, we will replace the
 *   node with a <a> element that should take the user to the kernel
 *   source location. */

NstDom.replaceKernelSource = function(node) {
    var srcName = NstDom.getTextContent(node);
    // If able to extract source name
    if (srcName) {
	// Build a <a> node
	var a = document.createElement("a");
	a.href = "http://lxr.linux.no/source/" + srcName;
	a.className = "kernelSource";
	a.target = "_blank";
	a.appendChild(node.firstChild); // document.createTextNode(srcName);
	a.onmouseover = function(e) {
	    NstDom.showKernelSourceToolTip(this, e);
	}

	// Replace old node with new <a> node
	node.parentNode.replaceChild(a, node);
    }
}

/* NstDom.spanEnhance()
 * 
 *   Run's one time after page load. Looks at the "class" attribute on
 *   all "<span>" entities and does the following:
 *
 *   "kernelSource" - If <span> contained a single child, entire span
 *   will be replaced with a <a> link to jump to the source code. */

NstDom.spanEnhance = function() {
  var spans = document.getElementsByTagName("span");
  for (var i = 0; i < spans.length; i++) {
    var span = spans[i];
    var cname = span.className;
    if (cname == "kernelSource") {
      NstDom.replaceKernelSource(span);
    } else if (cname == "sourceFile") {
      if (span.title) {
        NstDom.setSourceViewHandler(span, span.title, NstDom.getTextContent(span));
        span.title = null;
      }
    } else if (NstDom[cname + "SpanEnhance"]) {
      // Handle case where NstDom was updated with a new "ClassSpanEnhance" 
      // method (list NstWui might do)
      NstDom[cname + "SpanEnhance"](span);
    }
  }
}

/* NstDom.setCssClassAttr(classSelector, attr, val, omit)
 *
 * Function to set a CSS Class Attributes with javascript.
 * This function will step through each DOM 2 style sheet defined
 * on the page looking for the matching: 'classSelector' class
 * or style rule for the given 'attr' attribute and associated
 * 'val' value. If a match is found, the 'attr' attribute
 * will be set to 'val' value. If a match is not found and
 * the 'omit' option is set to false, the 'attr' attribute
 * and 'val' value will be added to the last style sheet
 * at the end of it.
 *
 * classSelector - Selector text for the class rule. This value
 *                 is case insensitive.
 *                 (e.g., 'div.niceTitle')
 * attr          - A class rule style sheet attribute.
 *                 (e.g., 'fontSize')
 * val           - The value to set the class rule style
 *                 sheet attribute to.
 *               - (e.g., '24px')
 * omit          - Set to true to omit adding the attribute and
 *                 value if a match was not found.
 *               - Set to false to add the attribute and value
 *                 if a match was not found.
 *
 * Returns: true  - a match was found and attribute set.
 *          false - no match was found and attribute was not set.
 *
 * Example 1: NstDom.setCssClassAttr('div.niceTitle', 'fontSize', '33px', true)
 *
 * Note 1:    If the Selector text for the class rule contains more than
 *            one name, separate each name with a comma and space:
 *
 * Example 2: NstDom.setCssClassAttr('h1.noteHeader, h1.bodyHeader', 'marginTop', '2.1em', true)
 *
 * Note 2:    Both Mozilla and Chrome group the entire Selector text for
 *            the class rule as one string where as IE breaks up the
 *            Selector text as individual class rules. This function handles
 *            this browser difference.
*/

NstDom.setCssClassAttr = function(classSelector, attr, val, omit) {
  //
  // set 'classSelector' case insensitive...
  var lc_cs = classSelector.toLowerCase();
  //
  // collection containing all the rules of the style sheet...
  var cssRules;

  //
  // If 'omit' was not specified do not add...
  if (omit == null) {
    omit = true;
  }

  // ********************
  // ***SET CLASS RULE***
  // ********************

  //
  // if IE...
  if (document.all) {
  //
  // style sheet collection name for IE...
    cssRules = 'rules';
  //
  // 
    var found_rule = false;
  //
  // Create a class selector text array in case of multiple
  // 'classSelector' names for IE...
    var ie_cs = lc_cs.split(/, /);
  //
  // Step thru page style sheets and class rules looking for
  // matching 'attr' attribute to set...
    for (var s = 0; s < document.styleSheets.length; s++) {
      for (var r = 0; r < document.styleSheets[s][cssRules].length; r++) {
        if (document.styleSheets[s][cssRules][r].selectorText) {
  //
  // check to set each individual class rule... 
          for (var c = 0; c < ie_cs.length; c++) {
            if (document.styleSheets[s][cssRules][r].selectorText.toLowerCase() == ie_cs[c]) {
              if (document.styleSheets[s][cssRules][r].style[attr]) {
                document.styleSheets[s][cssRules][r].style[attr] = val;
                found_rule = true;
              }
            }
          }
        }
      }
      if (found_rule) {
        return true;
      }
    }
  } else if (document.getElementById) {
  //
  // Other browsers (At least: Mozilla and Chrome)...
  //
  // style sheet collection name for Mozilla and Chrome...
    cssRules = 'cssRules';
  //
  // Step thru page style sheets and class rules looking for
  // matching 'attr' attribute to set...
    for (var s = 0; s < document.styleSheets.length; s++) {
      for (var r = 0; r < document.styleSheets[s][cssRules].length; r++) {
        if (document.styleSheets[s][cssRules][r].selectorText) {
          if (document.styleSheets[s][cssRules][r].selectorText.toLowerCase() == lc_cs) {
            if (document.styleSheets[s][cssRules][r].style[attr]) {
              document.styleSheets[s][cssRules][r].style[attr] = val;
              return true;
            }
          }
        }
      }
    }
  }

  // ********************
  // ***ADD CLASS RULE***
  // ********************

  //
  // If we get here then no class rule with attribute match was found...
  if (!omit) {
  //
  // Try to add class rule to the last style sheet at the end...
  var s = document.styleSheets.length - 1;
    if (document.styleSheets[s].insertRule) {
  //
  // Mozilla and Chrome add class rule method...
      try {
        document.styleSheets[s].insertRule(lc_cs + ' { ' + attr + ': ' + val + '; }', document.styleSheets[s][cssRules].length);
        return true;
      } catch (e) {
        return false;
      }
    } else if (document.styleSheets[s].addRule) {
  //
  // IE add class rule method...
      try {
  //
  // check to add each individual class rule at end of last style sheet...
        for (var c = 0; c < ie_cs.length; c++) {
          document.styleSheets[s].addRule(ie_cs[c], attr + ': ' + val + ';', -1);
        }
        return true;
      } catch (e) {
        return false;
      }
    }
  }
  return false;
}

/* NstDom.setInputSelection(n, start, nchars)
 *
 * Selects text within an <input> box (might work on <textarea> a
 * well).
 *
 * node - The DOM node to update (or ASCII ID of node).
 *
 * start - The starting position of the first character to hightlight
 * within the <input> box (0 corresponds to first character).
 *
 * nchars - Number of characters to highlight (must be greater than 0).
 */

NstDom.setInputSelection = function(node, start, nchars) {
  node = NstDom.getNode(node);
  if (node && (nchars > 0)) {
    // Compute end of selection
    var end = start + nchars;

    if (node.setSelectionRange) {
      // Firefox
      node.focus();
      node.setSelectionRange(start, end);
    } else if (node.createTextRange) {
      // IE
      var range = node.createTextRange();
      range.collapse(true);
      range.moveEnd('character', end);
      range.moveStart('character', start);
      range.select();
    }
  }
}

/*
 * ===== END: NstDom METHODS =====
 */


/* setContent(id, content)
 *
 * Sets the content of element having id of 'id' to the
 * specified 'content'.
 *
 * id - ID of element to update (if null or element not found, nothing done).
 *
 * content - New content (if null, nothing done). */
function setContent(id, content) {
  if ((content == null) || (id == null) || (!document.getElementById)) {
    return false;
  }

  var entity = document.getElementById(id);
  if (entity == null) {
    return false;
  }

  if (document.all) {
    //
    // Use non-DOM method (IE)
    entity.innerHTML = content;
  } else {
    //
    // Use DOM method (firefox)
    var rng = document.createRange();
    rng.setStartBefore(entity);
    while (entity.hasChildNodes()) {
      entity.removeChild(entity.lastChild);
    }
    entity.appendChild(rng.createContextualFragment(content));
  }

  return true;
}


/* getContent(id)
 *
 * Gets the content of element having id of 'id'.
 *
 * id - ID of element to get info from (if null or element not found,
 * nothing done).
 *
 * returns - Content (or null if not found). */
function getContent(id) {
  if ((id == null) || (!document.getElementById)) {
    return null;
  }

  var entity = document.getElementById(id);
  if (entity == null) {
    return null;
  }

  return entity.innerHTML;
}


//
// Replaces '&', '<', '>' and '"' with HTML escape codes.
function escapeHtml(s) {
  return s.replace('&','&amp;').replace('<','&lt;').replace('>','&gt').replace('"','&quot;');
}


/* Change class from 'odd'/'even' to 'oddover'/'evenover' for all
 * child elements.
 *
 * tr - A <tr> entity to modify (contained cells will be modified).
 *
 * evenover - Optional name of evenover class (defaults to 'evenover' if
 * omitted.
 *
 * oddover - Optional name of oddover class (defaults to 'oddover' if
 * omitted.
 */
function enterRow(tr, evenover, oddover) {
  if (oddover == null) oddover = 'oddover';
  if (evenover == null) evenover = 'evenover';

  var n = tr.cells.length;
  for (var i=0; i < n; i++) {
    entity = tr.cells.item(i);
    if (entity.className == 'odd') {
      entity.className = oddover;
    } else if (entity.className == 'labeledRow') {
      entity.className = 'labeledRowOver';
    } else {
      entity.className = evenover;
    }
  }
}


/* Change class from 'oddover'/'evenover' to 'odd'/'even' for all
 * child elements.
 *
 * tr - A <tr> entity to modify (contained cells will be modified).
 *
 * evenover - Not used.
 *
 * oddover - Optional name of oddover class (defaults to 'oddover' if
 * omitted.
 */
function exitRow(tr, evenover, oddover) {
  if (oddover == null) oddover = 'oddover';

  var n = tr.cells.length;
  for (var i=0; i < n; i++) {
    entity = tr.cells.item(i);
    if (entity.className == oddover) {
      entity.className = 'odd';
    } else if (entity.className == 'labeledRowOver') {
      entity.className = 'labeledRow';
    } else {
      entity.className = 'even';
    }
  }
}


//
// BEGIN - Cookie management functions...
//
// These set of functions are used to manage cookies within a web browser...

// setCookie(name, value, [expires], [path], [domain], [secure])
//        name - name of the cookie
//       value - value of the cookie
//   [expires] - optional: expiration date of the cookie
//               (defaults to end of current session)
//      [path] - path for which the cookie is valid
//               (defaults to path of calling document)
//    [domain] - domain for which the cookie is valid
//               (defaults to domain of calling document)
//    [secure] - Boolean value indicating if the cookie transmission requires
//               a secure transmission
//
//   Desscription: Cookie creation funtion...
//
//          Notes: 1) an argument defaults when it is assigned null as a placeholder
//                 2) a null placeholder is not required for trailing omitted arguments
//
//       Returns: N/A       
function setCookie(name, value, expires, path, domain, secure) {
  var curCookie = name
                  + "="
                  + escape(value)
                  + ((expires) ? "; expires="
                  + expires.toGMTString() : "")
                  + ((path) ? "; path=" + path : "")
                  + ((domain) ? "; domain=" + domain : "")
		  + ((secure) ? "; secure" : "");

  document.cookie = curCookie;
}


//
// getCookie(name)
//
//   name - name of the desired cookie
//
//   Description: return string containing value of specified cookie or null
//                if cookie does not exist...
//
//       Returns: The value of cookie 'name'...       
function getCookie(name) {
  var dc = document.cookie;
  var prefix = name + "=";
  var begin = dc.indexOf("; " + prefix);

  if (begin == -1) {
    begin = dc.indexOf(prefix);
    if (begin != 0) {
      return null;
    }
  } else {
    begin += 2;
  }

  var end = document.cookie.indexOf(";", begin);
  if (end == -1)
    end = dc.length;
  return unescape(dc.substring(begin + prefix.length, end));
}


//
// deleteCookie(name, [path], [domain])
//   name     - name of the cookie
//   [path]   - optional: path of the cookie (must be same as path used to create cookie)
//   [domain] - optional: domain of the cookie (must be same as domain used to create cookie)
//
//   Description: path and domain default if assigned null or omitted if no explicit
//                argument proceeds...
//
//       Returns: N/A       
function deleteCookie(name, path, domain) {
  if (getCookie(name)) {
    document.cookie = name
                    + "="
                    + ((path) ? "; path=" + path : "")
                    + ((domain) ? "; domain=" + domain : "")
                    + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
  }
}

//
// getCookieMinutes()
//
// Returns the life of a NST WUI cookie (in minutes).
function getCookieMinutes() {
  var mins = 43200;      // Default: 30 * 24 * 60 = 43200  => 30 days...

  //
  // Check for global NST session config timeout override...
  try {
    mins = parseInt(sysInfo['SESSION_CONFIG_TIMEOUT']);
  } catch (e) {
  }

  return mins;
}

//
// storeSessionId()
//
//   Description: This function creates a globally unique NST Session ID (sid).
//		  This session ID is used for server side configuration
//		  uniqueness so that mutiple browsers querying an NST
//		  probe can have their own configuration persu=istence data set.
//
//        Format: 'sid' format - "sid_" + "now.getTime()" + "_" + "6 digit random number"
//                Example: "sid_1134261488336_012345"
//
//          Note: Currently we are setting the 'sid' to expire in one month...
//
//       Returns: The value of the newly created 'sid'...       
function storeSessionId() {

  // expire the NST session ID cookie: "sid" 1 year from now...
  var etime = new Date();
  var random_num_str = "";

  // create a random number between: "0 - 999999"...
  var random_num = Math.round(Math.random() * 1000000);

  random_num_str = random_num.toString();

  // force 6 digit length prepending any necessary leading zeros...
  while (random_num_str.length < 6) {
    random_num_str = "0" + random_num_str;
  }

  // format: 'sid'...
  var sid = "sid_"
            + etime.getTime()
            + "_"
            + random_num_str;

  //
  // Set NST session cookie expiration based on cuurent
  // server time plus the session configuration timeout value...
  //
  // Note: getTime() - returns time in milliseconds...
  etime.setTime(etime.getTime() + getCookieMinutes() * 60 * 1000);

  //
  // create cookie...
  //
  // return cookies for 'secure' sessions only (HTTPS)...
  // setCookie('NST_Session_ID', sid, etime, '/', '', 'secure');
  //
  // return cookies for 'any' type of session...
  setCookie('NST_Session_ID', sid, etime, '/', '', '');

  var sid_get = getCookie('NST_Session_ID');

  // If we successfully stored a new cookie, reload the page
  if (sid_get) {
    window.location.reload();
  }

  return sid;
}


//
// getSessionId()
//
//   Description: This function will try to get the NST Session ID (sid)
//                for this browser. If the 'sid' is not found one
//                will be created. If a NST Session ID is found, update
//                the 'Cookie' session expiration time to the current
//                server time plus the session configuration timeout
//                value. Also update the PHP Session ID 'Cookie'
//                expiration time...
//
//       Returns: The value of a new or current NST Session ID...       
function getSessionId() {

  //
  // Update NST session ID...
  var sid = getCookie("NST_Session_ID");

  // Update the NST Session ID cookie expiration time if able to get ID...
  if (sid) {
    var etime = new Date();
  //
  // Set NST session ID cookie expiration based on current
  // server time plus the session configuration timeout value...
    etime.setTime(etime.getTime() + getCookieMinutes() * 60 * 1000);
    setCookie('NST_Session_ID', sid, etime, '/', '', '');

  //
  // Also update PHP session ID cookie expiration time if available...
    var pid = getCookie("PHPSESSID");
    if (pid) {
      setCookie('PHPSESSID', pid, etime, '/', '', '');
    }
  } else {
    return unescape(storeSessionId());
  }
  return unescape(sid);
}

// END - Cookie management functions...


//
// basename(path)
//
//   Determines the base file name given a full path.
function basename(path) {
  var spos = path.lastIndexOf("/");
  if (spos != -1) {
    return path.substring(spos + 1);
  }
  return path;
}


//
// toggleVisibility(id)
//
//   Toggles the CSS 'display' attribute from 'block' to 'none' (or vice
//   versa to make a block of HTML output visible (or invisible). Useful
//   when creating "pull-downs".
function toggleVisibility(id) {
  var entity = document.getElementById(id);
  if (entity.style.display == 'none') {
    entity.style.display = 'block';
  } else {
    entity.style.display = 'none';
  }
}


//
// toggleOpenClose(iconid, id)
//
// Toggles visibility of block identified by 'id' and changes icon
// image (22x22) between dir-close.gif and dir-open.gif. Useful in
// creating areas that look like directory trees that can be opened and
// closed.
function toggleOpenClose(iconid, id) {
  var entity = document.getElementById(id);
  var icon = document.getElementById(iconid);
  if (entity.style.display == 'none') {
    entity.style.display = 'block';
    icon.src = '/nst/images/folder.open.gif';
  } else {
    entity.style.display = 'none';
    icon.src = '/nst/images/folder.gif';
  }
}


//
// setOpenClose(iconid, id, displayState)
//
// Set visibility of block identified by 'id' and changes icon
// image (22x22) between dir-close.gif and dir-open.gif.
//
// displayState - 'none' - close or 'display' - open...
function setOpenClose(iconid, id, displayState) {
  var entity = document.getElementById(id);
  var icon = document.getElementById(iconid);
  if (displayState == 'none') {
    entity.style.display = 'none';
    icon.src = '/nst/images/folder.gif';
  } else {
    entity.style.display = 'block';
    icon.src = '/nst/images/folder.open.gif';
  }
}


//
// getCheckedRadioValue(radioButton)
//
// Iterates through all of the radio buttons looking for the first
// checked (should only be one) and returns the associated value.
function getCheckedRadioValue(rb) {
  for (var i = 0; i < rb.length; i++) {
    if (rb[i].checked) {
      return rb[i].value;
    }
  }
  return ""; // Default to empty string if nothing checked
}


//
// getSelectedValueById(select_id)
//
//   Finds selected value of the <select id="select_id"> component
//   on the page (only works for selects where one item can be selected).
//
//   select_id - The id attribute assigned to the <select> entity.
function getSelectedValueById(select_id) {
  var e = document.getElementById(select_id);
  return e.options[e.selectedIndex].value;
}


//
// Parses arguments from URL location (uses current page if null
// passed).  Stores results in nave/value pairs returned as an
// object. From "JavaScript The Definitive Guide".
//
// u - URL Location to parse (optional)
//
// Example:
// var args = getArgs();
// if (args.pkg) {
//   document.write("<p>Package: <b>"+args.pkg+"</b></p>");
// }
function getArgs(u) {
  if (u == null) u = location;	// default to current page

  var args = new Object();
  var query = u.search.substring(1);	 // get query string
  var pairs = query.split(",");

  for(var i= 0; i < pairs.length; i++) {
    var pos=pairs[i].indexOf('=');	// look for name=value
    if (pos == -1) continue;
    var argname = pairs[i].substring(0,pos);
    var value = pairs[i].substring(pos+1);
    args[argname] = unescape(value);
  }
  return args;
}


//
// Inserts a section link anchor point (place to jump to)
// and then increments the global sectCnt variable.
function addSectionLink() {
  var id = "sectLink" + sectCnt;
  document.writeln("<a id=\"" + id + "\" name=\"" + id + "\"></a>");
  NstDom.registerSection(id);
  sectCnt++;
}


//
// Writes out nav link information
//
// url - Where to jump to
// idir - Where to find images (defaults to /nst/images/ if omitted)
// bitmap - name of bitmap
//
// text - Text to display as hover help (no double quotes), OR (it
// textWidth parameter omitted), the JavaScript function to call on
// the onmouseover event.
//
// w - width of bitmap
// h - height of bitmap
// onclick - optional onclick handler
//
// textWidth - width of domTT tooltip in pixels (if present, it will
// be used as the width of the the DOM tooltip).
function addNavLink(url,idir,bitmap,text,w,h,onclick,textWidth) {

  if ( idir == null ) {	// Where to location images
    idir="/nst/images/";
  }

  var aattrs=" class=\"navLinkA\"";
  if (url != null) {
    aattrs=aattrs + " href=\""+url+"\"";  
  }
  if (onclick != null) {
    aattrs=aattrs + " onclick=\""+onclick+"\" style=\"cursor: pointer;\"";
  }

  if (textWidth) {
    document.write("<a" + aattrs + " onmouseover=\"domTT_activate(this, event, 'content', '"+text+"', 'width', "+textWidth+");\">");
  } else {
    document.write("<a" + aattrs + " onmouseover=\"" + text + ";\">");
  }

  document.write("<img alt=\"\" src=\"" + idir + bitmap
	+ "\" width=\"" + w
	+ "\" height=\"" + h
 	+ "\" class=\"navLinkImg\" />");

  document.write("</a>");
}


//
// Set the "Home Icon" tooltip, URL and offset within page
//
// url - URL to jump to
// tooltip - Tool tip to display
// [tag] - Optional Name of anchor to jump to (the "tag" in <a name="tag"></a>)
navLinkStartToolTip = "<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>NST Start Page</span>\\\&#39; Page";
navLinkHomeToolTip = "<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>NST WUI Index</span>\\\&#39; Page";
navLinkHomeUrl = "/";
navLinkBreadCrumbs = false;

function setNavLinkHome(url, tooltip, tag) {
  // Not legal to change if bread crumbs are enabled
  if (!navLinkBreadCrumbs) {
    if (tooltip != "") {
      navLinkHomeToolTip = tooltip;
    }
    navLinkHomeUrl = url;
    if (tag != null) {
      navLinkHomeUrl = navLinkHomeUrl + "#" + tag;
    }
  }
}


//
// Standard nav links (top, bottom, next, previous)
//
// idir - Root location to find bitmap images at (defaults to "/nst/images/"
// img - Optional bitmap file name to use (if you don't want default)
// cnt (next/prev only) - index of section to jump to (optional)
// url - Link to jump to (or null for default)
function addNavLinkHome(bitmap,idir,url) {
  if (bitmap == null) {
    bitmap="links_home.gif";
  }

  if (url == null) {
    url = navLinkHomeUrl;
  }

  addNavLink(url, idir, bitmap, navLinkHomeToolTip, 21, 22, null, 220);
}

function addNavLinkHomeFooter(bitmap, idir, url) {

  if (bitmap == null) {
    bitmap="links_footerhome.gif";
  }

  if (url == null) {
    url = navLinkHomeUrl;
  }

  addNavLink(url, idir, bitmap, navLinkHomeToolTip, 20, 23, null, 220);
}

function positionReload(hash) {

  // If page doesn't support smart reloads, let browser
  // decide what to do.
  if (!smartReload) {
    window.location.reload();
    return;
  }

  //
  // "#sectLink0" is alias for "#top"
  if (hash == "#sectLink0") {
    hash = "#top";
  }

  //
  // special refresh after submit post or non-submit (link)...
  if (window.location.search.length == 0) {
    var url = window.location.pathname + "?forcerefresh=" + startTime + hash;
    window.location.replace(url);
    return;
  }

  if (isIE()) {
    // Ugly hack for Internet Explorer

    var newloc = window.location.href;

    // If URL contains a "?#" or a '?' at end, trim it off - we don't
    // need it (and we keep getting it when the submit form hack is used).

    var hpos = newloc.indexOf("?#");
    if (hpos > 0) {
      newloc = newloc.substr(0,hpos);
    } else {
      hpos = newloc.indexOf("?");
      if (hpos == (newloc.length - 1)) {
        newloc = newloc.substr(0,hpos);
      }
    }

    // If URL contains form data information, we just tell browser to reload

    if (newloc.indexOf('?') >= 0) {

      // Trim off any trailing "#TAG" string
      hpos = newloc.lastIndexOf("#");
      if (hpos > 0) {
        newloc = newloc.substring(0,hpos);
      }

      // Trim off any trailing "forcerefresh=XXX" that may have been added
      var tpos = newloc.indexOf('forcerefresh=');
      if (tpos > 0) {
        newloc = newloc.substr(0, tpos - 1);
      }
    
      // Tack on new "&forcerefresh=XXX#TAG"
      if (newloc.indexOf('?') > 7) {
        newloc += '&';
      } else {
        // If no other parameters, then use '?' as its the first parameter
        newloc += '?';
      }

      newloc = newloc + 'forcerefresh=' + startTime + hash;
      window.location.replace(newloc);

    } else {

      // Since URL didn't contain any form values, we can use the submit form
      // hack to set the location to the current section.

      hpos = newloc.lastIndexOf("#");
      if (hpos > 0) {
        newloc = newloc.substring(0,hpos);
      }
      newloc += hash;
      document.forms.ieReloadHack.action = newloc;
      document.forms.ieReloadHack.submit();
    }

  } else {

  //
  // This is how its suppose to work...
    window.location.replace(hash);
    window.location.reload();
  }
}


//
// Reload the current window with the page...
function reloadWindow() {

  // If page doesn't support smart reloads, let browser
  // decide what to do.
  if (!smartReload) {
    window.location.reload();
    return;
  }

  //
  // special refresh after submit post or non-submit (link)...
  if (window.location.search.length == 0) {
    var url = window.location.pathname + "?forcerefresh=" + startTime;
    window.location.replace(url);
    return;
  }

  window.location.reload();
}


//
// Reload a frame in current window if the
// frame is found...
function reloadWindowFrame(frame) {

  if (window.frames[frame]) {
    window.location=window.frames[frame].location.href;
  } else {
    window.location.reload();
  }
}

function addNavLinkReload(bitmap,idir,cnt) {
  if (cnt == null) cnt = sectCnt-1;
  if (bitmap == null) bitmap="links_reload.gif";

  var tooltip = "<span style=\\\&#39;color: #cc9900;\\\&#39;>Reload</span> The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Current</span>\\\&#39; Page At This <span style=\\\&#39;color: #00ccff;\\\&#39;>Position</span>";
  var tooltipwidth = 280;

  if (!smartReload) {
    tooltip = "<span style=\\\&#39;color: #cc9900;\\\&#39;>Reload</span> Contents Of The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Current</span>\\\&#39; Page";
    tooltipwidth = 260;
  }

  addNavLink(null, idir, bitmap, tooltip, 23, 22, "positionReload('#sectLink"+cnt+"')", tooltipwidth);
}

function addNavLinkToggleConsole(bitmap,idir) {
  try {
    if (!window.NstConsole) {
	return;
    }

    if (bitmap == null) bitmap="toggle_console.gif";
    addNavLink(null, idir, bitmap, "NstDom.showNstConsoleTooltip(this, event)", 19, 22, "NstConsole.toggleConsole()", false);
  } catch (e) {
  }
}

function addNavLinkTop(bitmap,idir) {
  if (bitmap == null) bitmap="links_top_arrow.gif";
  addNavLink(null,idir,bitmap,"<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Top</span>\\\&#39; Of The Page",15,23,"NstDom.scrollTo(document.body, true)",200);
}

function addNavLinkTopFooter(bitmap,idir) {
  if (bitmap == null) bitmap="links_footertop_arrow.gif";
  addNavLink(null,idir,bitmap,"<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Top</span>\\\&#39; Of The Page",17,23,"NstDom.scrollTo(document.body, true)",200);
}

function addNavLinkBottom(bitmap,idir) {
  if (bitmap == null) bitmap="links_bottom_arrow.gif";
  addNavLink(null,idir,bitmap,"<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Bottom</span>\\\&#39; Of The Page",15,23,"NstDom.scrollToBottom(document.body, 0, 0)",220);
}

function addNavLinkNext(bitmap,idir,cnt) {
  if (cnt == null) cnt = sectCnt;
  if (bitmap == null) bitmap="links_down_arrow.gif";
  var url = "#sectLink" + cnt;
  // var url = "javascript:NstDom.moveToNextSection('sectLink" + (cnt - 1) + "');";
  addNavLink(url,idir,bitmap,"<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Next</span>\\\&#39; Section",15,20,null,180);
}

function addNavLinkPrev(bitmap,idir,cnt) {
  if (cnt == null) cnt = (sectCnt-2);
  if (bitmap == null) bitmap="links_up_arrow.gif";

  // Jump to top unless count is not 0
  var prev = "#top";
  if (cnt != 0) {
    prev = "#sectLink" + cnt;
  }
  addNavLink(prev,idir,bitmap,"<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Previous</span>\\\&#39; Section",15,20,null,200);
}

function addNavLinkPrevFooter(bitmap,idir,cnt) {
  if (cnt == null) cnt = (sectCnt-2);
  if (bitmap == null) bitmap="links_footerprev_arrow.gif";
  addNavLink("#sectLink"+cnt,idir,bitmap,"<span style=\\\&#39;color: #cc9900;\\\&#39;>Go</span> To The \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>Previous</span>\\\&#39; Section",17,23,null,200);
}


//
// Adds a section header to document:
//
// t - Title to display (may be blank)
// r - Optional anchor point (if you need to refer back)
// h - Optional header level (defaults to "h2" if omitted)
//   - This option is no longer used...
// idir - Directory with bitmaps (defaults to "/nst/images/" if omitted)
// homeurl - Address of home page
needReloadFormForIE = true;

function addSectionHeader(t,r,h,idir,homeurl) {

  if (isIE() && needReloadFormForIE) {
    needReloadFormForIE = false;
    document.writeln("<form name='ieReloadHack' action='MISSING'></form>");
  }

  if ( h == null ) {	// Default to level 2 header
    h = "h2";
  }

  var sectPrior = sectCnt-1; // Get previous section number

  if ( r != null ) {	// Insert anchor (name and id) if user passed one
    document.writeln("<a name=\""+r+"\" id=\""+r+"\"></a>")
  }

  if (t == null) {	// Permit blank titles
    t = "";
  }

			// Big Hairy block of HTML
//  document.write("<"+h+">");

  addSectionLink();      // Set our anchor point and increment section number

  document.write(
	"<table class=\"navheadertext\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"+
         "<colgroup>"+
          "<col width=\"99%\">"+
          "<col width=\"1%\">"+
         "</colgroup>"+
	 "<tr>"+
	  "<td align=\"left\" style=\"padding-left: .2em;\">"+
	    t+
	  "</td><td align=\"right\" valign=\"bottom\" style=\"padding-right: .2em;\">"+
            "<span style=\"white-space: nowrap;\">");
  addNavLinkHome(null,idir,homeurl);
  addNavLinkReload(null,idir);
  addNavLinkTop(null,idir);
//  document.write("</td><td>");
  addNavLinkBottom(null,idir);
//  document.write("</td><td align=\"right\">");
  addNavLinkPrev(null,idir);
//  document.write("</td><td align=\"right\">");
  addNavLinkNext(null,idir);
  document.write("</span></td></tr></table>");
//	"</"+h+">");

}


//
// Adds a "collapsible" section header to document:
//
// title   - Title to display (may be blank)
// id      - The ID (for reference) for the section - must be unique
//           Also: The <div> section to toggle visibility on Must have
//           the ID of: [id + "-area"]
// istate  - Initial state ('none' for hidden, 'block' for display)
// hlevel  - Optional header level (defaults to "h2" if omitted)
//         - This option is no longer used...
// idir    - Directory with bitmaps (defaults to "/nst/images/" if omitted)
// homeurl - Address of home page
function addCollapsibleHeader(title, id, istate, hlevel, idir, homeurl) {

  var headerIcon = "<img id=\""
    + id + "-icon\" onmouseover=\"domTT_activate(this, event, 'content', '<span style=\\\&#39;color: #cc9900;\\\&#39;>Click</span> on icon to \\\&#39;<span style=\\\&#39;color: #33ff00;\\\&#39;>hide</span>/<span style=\\\&#39;color: #33ff00;\\\&#39;>show</span>\\\&#39; contents', 'width', 240);\""
    + " width=\"27\" height=\"22\" alt=\"\""
    + " style=\"border: none; margin-right: 5px; vertical-align: baseline; cursor: pointer;\""
    + " onclick=\"javascript:toggleOpenClose('" + id + "-icon', '"
    + id + "-area');\""
    + " src=\"/nst/images/";

  if (istate == 'none') {
    headerIcon += "folder.gif";
  } else {
    headerIcon += "folder.open.gif";
  }
  headerIcon += "\" />";

  // Print header block for table
  addSectionHeader(headerIcon + title, id, hlevel, idir, homeurl);
}

//
// Calculate an average DOM Tooltip length in pixels...
//
// val      - Value (string) name or (integer) number of chars in string...
// offset   - optional fixed character length offset value in (pixels)...
// minWidth - optional minimum width value (pixels) returned...
// maxWidth - optional maximum width value (pixels) returned...
//
// Return: an average DOM Tooltip length in pixels...
function calcDomTTLen(val, offset, minWidth, maxWidth) {

  var olen = 0;
  var vlen = 0;

  //
  // has a "val" param been passed?
  if (val != null) {
  //
  // determine value type...
    if (val.length) {
  //
  // calc value length (string)...
      vlen = val.length;
    } else {
  //
  // set value length (integer)...
      vlen = val;
    }
  }

  //
  // check for optional offset...
  if (offset != null) {
    olen = offset;
  }

  //
  // calc length using DomTT multipler for javascript...
  var clen = olen + (vlen * 7.9);

  //
  // return no less than "minWidth" width value if specified...
  if (minWidth != null) {
    if (clen < minWidth) {
      return minWidth;
    }
  }

  //
  // return up to "maxWidth" width value if specified...
  if (maxWidth != null) {
    if (clen > maxWidth) {
      return maxWidth;
    }
  }

  //
  // return an integer pixel value...
  return Math.round(clen);
}

// Optional declare entries into the 'sysInfo' array...
// sysInfo["NST_ISO_VERSION"]    - Version of ISO (like: "1.6.0")
// sysInfo["NST_ISO_BUILD_DATE"] - ISO Build date (text)
// sysInfo["NST_ISO_BUILD_DATE_SHORT"] - ISO Build date short format (text)
// sysInfo["NST_ISO_BUILD_DATE_SECS"] - ISO Build date as time_t (int)
// sysInfo["NST_WUI_VERSION"]    - Version of WUI (like: "1.6.0")
// sysInfo["NST_WUI_BUILD_DATE"] - WUI Build date (text)
// sysInfo["NST_WUI_BUILD_DATE_SHORT"] - WUI Build date short format (text)
// sysInfo["NST_WUI_BUILD_DATE_SECS"] - WUI Build date as time_t (int)
// sysInfo["NST_LIVECD_BOOT"] - true if booted from Live CD, false if hard disk
// sysInfo["NST_VM_BOOT"] - true if booted in VMware, false if not
// sysInfo["KERNEL"]             - Kernel information
// sysInfo["CPU_COUNT"]          - Number of CPUs/Cores
// sysInfo["CPU_MODEL"]          - CPU Model info
// sysInfo["CPU_SPEED"]          - CPU speed (floating point MHz)
// sysInfo["CPU_BOGOBIPS"]       - Bogo BIPS value (Bogo MIPS / 1000.0)
// sysInfo["CPU_CACHE"]          - String describing CPU cache
// sysInfo["RAM_TOTAL"]          - Total amount of RAM (in MB)

// Dynamic information that might appear in sysInfo...
// sysInfo["HOST_NAME"]     - Primary ASCII host name associate with system
// sysInfo["IP_ADDRESS"]    - Primary IP address associated with system

// sysInfo["PAGE_SERVER_TIME"]   - Time of page creation at NST server...
// sysInfo["Server Create Time"] - Time of page rendering at client browser...

// sysInfo["UP_FORMAT"]     - Describe the format of the up time field on nav bar
// sysInfo["UP_DAYS"]       - Number of days (int) system has been up
// sysInfo["UP_HHMM"]       - Hours and minutes system up for current day

// sysInfo["LOAD_1MIN"]     - System load from last 1 minute period
// sysInfo["LOAD_5MIN"]     - System load from last 5 minute period
// sysInfo["LOAD_15MIN"]    - System load from last 15 minute period

// sysInfo["RUNNING_THREADS"]
//   Number of running threads on system (from /proc/loadavg)

// BEGIN fields set by AJAX reqeust of /nstwui/php/system/cpu-usage-ajax.php

// sysInfo["PROC_START"]    - Time of 1st reading of /proc/stat (seconds)
// sysInfo["PROC_END"]      - Time of 2nd reading of /proc/stat (seconds)

// sysInfo["PROC_FORKED"]   - Total number of processes forked by system
// sysInfo["PROC_RUNNING"]  - Processes currently running
// sysInfo["PROC_BLOCKED"]  - Processes currently blocked from running

// sysInfo["CPU_USAGE_COUNT"] - Number of CPUs we have usage information for.
//   NOTE: If set to a value larger than 0, we will have information for each 
//   CPU (CPU0, CPU1, ...), plus "summary" usage with the key of just "CPU".

// sysInfo["CPU"] - Summary usage
// sysInfo["CPU0"] - Usage for first core
// sysInfo["CPU1"] - Usage for second core 
// ...

// Each CPU usage summary is an array of the following:
// sysInfo["CPU"]["USAGE"] - Total usage in range of [0.0, 1.0]
// sysInfo["CPU"]["USER"] - Counts attributed to user time
// sysInfo["CPU"]["USER_NICE"] - Counts attributed to user "nice" time
// sysInfo["CPU"]["SYSTEM"] - Counts attributed to system time
// sysInfo["CPU"]["IDLE"] - Counts attributed to idle time
// sysInfo["CPU"]["IO_WAIT"] - Counts attributed to I/O wait time
// sysInfo["CPU"]["HARD_IRQ"] - Counts attributed to servicing hard IRQs
// sysInfo["CPU"]["SOFT_IRQ"] - Counts attributed to servicing soft IRQs
// sysInfo["CPU"]["STOLEN"] - Counts attributed to stolen virtualization time
// sysInfo["CPU"]["TOTAL"] - Total of all counts

// sysInfo["SESSION_CONFIG_TIMEOUT"]
//
//   How long session and cookie information should persist in minutes.

// END fields set by AJAX reqeust of /nstwui/php/system/cpu-usage-ajax.php

/* Function to build a single row in a tooltip table which will have the form:
 *
 * <tr>
 *   <th align="left">LABEL:</th>
 *   <td align="left"><span class="ttValue">VALUE</span></td>
 * </tr>
 *
 * NOTE: If either the LABEL or VALUE are null, then a empty string is returned.
 *
 * label - Label to appear for table row
 * value - Value to appear in table row
 */

//
// Global var used to hold the maximum char width of a "value"
// in a "keyLabels" array...
var keyLabelMaxDomTTWidth = 0;

function buildInfoTableRow(label, value) {
  if (label && value) {

  //
  // see if max char width in array...
  //
  // Note: Use a javascript reg expr: "/ +/g" to remove duplicate
  //       spaces in string when calculating the value len for HTML
  //       rendering of multiple spaces...
    if (value.toString().replace(/ +/g," ").length > keyLabelMaxDomTTWidth) {
      keyLabelMaxDomTTWidth = value.toString().replace(/ +/g," ").length;
    }

  //
  // highlight chars: '%,(,)'
    label = label.replace(/\(/,"<span style='color: white; font-weight: normal;'>(</span>");
    label = label.replace(/%/,"<span style='color: white; font-weight: normal;'>%</span>");
    label = label.replace(/\)/,"<span style='color: white; font-weight: normal;'>)</span>");

  //
  // build key/value DomTT row...
    return '<tr><th align="right"><span class="ttNote">' + label
	+ '</span><span class="ttNormal">:</span></th>'
        + '<td align="left"><span class="ttValue">'
	+ value + '</span></td></tr>';
  }
  return "";
}

/* Function to build a entire table which will have the form:
 *
 *   <tr>
 *     <th class="ROW[0,0]">ROW[1,0]</th>
 *     <th align="ROW[0,1]">ROW[1,1]</th>
 *     ...
 *   </tr>
 *   <tr>
 *     <td class="ROW[2,0]">ROW[3,0]</td>
 *     <td class="ROW[2,1]">ROW[3,1]</td>
 *     ...
 *   </tr>
 *   <tr>
 *     <td class="ROW[2,0]">ROW[4,0]</td>
 *     <td class="ROW[2,1]">ROW[4,1]</td>
 *     ...
 *   </tr>
 *   ...
 * </tr>
 *
 * You pass this function an array of arrays like the following:
 *
 * var rows = [
 *   [ "ttHeadRight", "ttHeadRight", "ttHeadRight" ],
 *   [ "Year", "Rate", "Balance" ],
 *   [ "ttValue", "ttValue", "ttValue" ],
 *   [ 2006, "4.5%", 1000 ],
 *   [ 2007, "4.6%", 1045 ]
 * ];
 *
 * The first row indicates the class for each of the <td> elements.
 * The second row is treated as a header row (<th>), but ONLY if the
 * 'header' parameter is set to true.
 *
 * NOTE: If either the LABEL or VALUE are null, then a empty string is returned.
 *
 * data - Array of rows of information for table.
 * header - Pass true if second row is a row of headers (th), pass false
 * if its data.
 */

function buildTableRows(table, header) {
  var results = "";
  if (!table || (table.length < 2)) {
    return results;
  }

  var classes = table[0];

  for (var i = 1; i < table.length; i++) {
    var row = table[i];
    results += "<tr>";

    for (var j = 0; j < row.length; j++) {
      if ((i == 1) && header) {
	results += "<th class=\"" + classes[j] + "\">" + row[j] + "</th>";
      } else {
	results += "<td class=\"" + classes[j] + "\">" + row[j] + "</td>";
      }
    }

    // If header, printed, load classes for data (and skip classes row)
    if (header && (i == 1)) {
	i++;
	classes = table[i];
    }

    results += "</tr>";
  }
  return results;
}

/* Function to build a HTML table of key/value pairs from the sysInfo[]
 * array.
 *
 * keyLabels - An array of KEY/LABEL pairs like:
 *
 *   var kl = new Array(KEY0, LABEL0 [, KEY1, LABEL1 [, ...]]);
 *
 * For example:
 *
 *   var kl = new Array("LOAD_1MIN", "Load 1 Min",
 *                      "LOAD_5MIN", "Load 5 Min");
 *
 * We then generate a HTML table with a row for each KEY/LABEL pair which
 * we find a value for sysInfo[KEY] defined. If we don't find ANY defined
 * values, the empty string is returned.
 *
 * calcMaxValLen - if set to "true", the max charater count for the
 *                 largest value in "keyLabels" is returned... 
 *
 * Note: To determine the "value" with the max char width, the
 *       the global var: "keyLabelMaxDomTTWidth" will be zeroed out. */

function buildSysInfoTable(keyLabels, calcMaxValLen) {
  var html = "<table>";
  var cnt = 0;

  //
  // new table: zero out max char width value var...
  keyLabelMaxDomTTWidth = 0;

  for (var i = 0; i < keyLabels.length; i += 2) {
    var key = keyLabels[i];
    var label = keyLabels[i+1];
    if (sysInfo[key]) {
      cnt++;
      html += buildInfoTableRow(label, sysInfo[key]);
    }
  }

  //
  // return max char width value if true...
  if ((calcMaxValLen != null) && calcMaxValLen) {
    return keyLabelMaxDomTTWidth;
  }

  // If nothing found, return nothing
  if (cnt == 0) {
    return "";
  }

  html += "</table>";
  return html;
}

//
// Set dynamic tooltip (TT) for 'NST Distribution Release' info
// on this NST probe...

function nstDistoReleaseTT() {
  var nstDistoRelease = "<div class='line1px'>"
                      + "&#39;<span style='color: #33ff00;'>"
                      + "NST"
                      + "</span>&#39;"
                      + " Version Information</div>";

  // NOTE: Only the available keys will appear in the output table
  // (either WUI or HTML, but not both)
  var keyLabels = new Array(
    "NST_WUI_FULL_VERSION", "WUI Version",
    "NST_WUI_BUILD_DATE", "WUI Build Date",
    "NST_HTML_VERSION", "Web Site Version",
    "NST_HTML_BUILD_DATE", "Web Site Build Date",
    "NST_ISO_VERSION", "Distribution Version",
    "NST_ISO_BUILD_DATE", "Distribution Build Date",
    "NST_ISO_DEV_VERSION", "Development Version");

  nstDistoRelease += buildSysInfoTable(keyLabels);

  return nstDistoRelease;
}

//
// Functions for a DOM node font-size increase/decrease...

//
// nodeIncreaseFontSize(nodeid,initFontSize,increment,max)
//
//        nodeid - DOM node id...
//  initFontSize - Optional Initial font-size (string) expressed as a percentage...
//     increment - Optional incremental percentage size (default: "10%")...
//           max - Optional maximum font size percentage value (default: "10000"%)...
function nodeIncreaseFontSize(nodeid,initFontSize,increment,max) {

  //
  // set initial vars...
  var node = document.getElementById(nodeid);

  //
  // adjust default font-size based on browser...
  var initfs = isIE() ? 80 : 100;
  var fsval = initfs;
  if (initFontSize != null) {
    if (initFontSize != "") {
      fsval = parseInt(initFontSize);
    }
  }

  var inc = 10;
  if (increment != null) {
    inc = increment;
  }
  var maxval = 10000;
  if (max != null) {
    maxval = max;
  }

  if (node) {
    if(node.style.fontSize) {
  //
  // get current font-size value and remove "%" or "inherit"...
      var curfs = node.style.fontSize.replace("%","");
      curfs = parseInt(curfs.replace("inherit",""));
      if (curfs != "") {
        fsval = parseInt(curfs);
      }
    }
  //
  // increment and range limit check...
    fsval = fsval + inc;
    if (fsval > maxval) {
      fsval = maxval;
    }
    node.style.fontSize = fsval + "%";
  }
}

//
// nodeDecreaseFontSize(nodeid,initFontSize,decrement,min)
//
//        nodeid - DOM node id...
//  initFontSize - Optional Initial font-size expressed as a percentage...
//     decrement - Optional decremental percentage size (default: "10%")...
//           max - Optional minimum font size percentage value (default: "10"%)...
function nodeDecreaseFontSize(nodeid,initFontSize,decrement,min) {

  //
  // set initial vars...
  var node = document.getElementById(nodeid);

  //
  // adjust default font-size based on browser...
  var initfs = isIE() ? 80 : 100;
  var fsval = initfs;
  if (initFontSize != null) {
    if (initFontSize != "") {
      fsval = parseInt(initFontSize);
    }
  }

  var decr = 10;
  if (decrement != null) {
    decr = decrement;
  }
  var minval = 1;
  if (min != null) {
    minval = min;
  }

  if (node) {
    if(node.style.fontSize) {
  //
  // get current font-size value and remove: "%" or "inherit" from results...
      var curfs = node.style.fontSize.replace("%","");
      curfs = parseInt(curfs.replace("inherit",""));
      if (curfs != "") {
        fsval = parseInt(curfs);
      }
    }
  //
  // decrement and range limit check...
    fsval = fsval - decr;
    if (fsval < minval) {
      fsval = minval;
    }

    node.style.fontSize = fsval + "%";
  }
}


/* Class to help work with IP addresses. */

function NstIp(ip) {
  this._Ip = ip;
}

/* Get the IP Address/Host Name associated with the object. */

NstIp.prototype.getIp = function() {
  return this._Ip;
}


/* Get URL to show IP address on map. */

NstIp.prototype.getMapUrl = function() {
  return "http://geotool.servehttp.com/?ip=" + this.getIp();
}

/* Open a new window showing location of IP/Address on map. */

NstIp.prototype.showMapLocation = function() {
  window.open(this.getMapUrl(),"Host_Map",
              "height=620,width=820,toolbar=nodirectories=no,status=no,"
              + "menubar=no,scrollbars=yes,resizable=yes");
}

/* Display a DOM Tooltip for a link that will open the IP/Address on map.
 *
 * node - Node which triggered the event.
 * event - JavaScript event. */

NstIp.prototype.showMapToolTip = function(node, event) {

  var content = NstDom.ttNote("Show")
    + " the " + NstDom.ttEmphasis("Location", true)
    + " associated with:\n\n  "
  + NstDom.ttValue(this.getIp(), true);

  domTT_activate(node, event,
                 'content', content,
                 'width', 320);
}

/* Static method to automatically "hover enhance" a IP address.
 *
 * Assigns a tool tip and makes item "clickable" to show map. */

NstIp.hoverMapEnhance = function(node, event, ip) {
  if (!ip) {
    ip = node.firstChild.data;
  }
  // Update node
  node._NstIp = new NstIp(ip);
  node.onclick = function() {
    this._NstIp.showMapLocation();
  }

  node.onmouseover = function(event) {
    this._NstIp.showMapToolTip(this, event);
  }

  // Activate tooltip
  node._NstIp.showMapToolTip(node, event);
}
