/* $Id: console.js,v 1.69 2009/05/14 19:16:46 pblankenbaker Exp $
 *
 * document.NstConsole
 *
 * This singleton (document.NstConsole) provides a "mini-console"
 * where one can evaluate JavaScript code within any web page.
 *
 * Once this class is loaded, one can make use of the various NstConsole
 * methods. In particular:
 *
 *   NstConsole.toggleConsole();
 *   NstConsole.clear(inputArea, outputArea);
 *   NstConsole.echo("Header", false, "nstConsoleEvalOutput");
 *   NstConsole.echo("Plain Text");
 *   NstConsole.var_dump(new Date());
 *
 * One can tell if this object is available via:
 *
 * if (window.NstConsole) {
 *   alert("NstConsole available");
 * } else {
 *   alert("NstConsole not loaded");
 * }
 *
 * See the file "wui/css/console.css" for the CSS used to "style" the console.
 *
 * To add to the bottom of the button area, redefine the
 * "NstConsole.createFooterArea" function with your own block of code
 * to add to the "parentNode" passed. For example:
 *
 * NstConsole.createFooterArea = function() {
 *   return document.createTextNode("Hello");
 * }
 */

var NstConsole = new function() {

  // DOM ready for Mozilla browsers...
  this._DOMReady = false;

  // Set debug level
  this._DebugLevel = 0;

  // The insertion point where (the node will be appended to)
  this._InsertPoint = false;

  // Initialize debug line counter & entity node counter
  this._DebugCounter = 0;
  this._EntityCnt = 0;

  // Output/Input areas (created as needed)
  this._Console = null;
  this._OutputDiv = null;
  this._InputDiv = null;

  // Array of cached echo objects
  this._EchoCached = [ ];

  // Set line length
  this._LineLength = -1;

  // Whether or not we are in one line mode
  this._OneLineMode = false;

  // Number of pixels to subtract off from output height/width
  // to prevent overflow
  this._OutputHeightTweak = 0;
  this._OutputWidthTweak = 0;

  // get current navigator brower ID info..
  this._UserAgent = navigator.userAgent.toLowerCase();

  // Set minimum height (in pixels) for console output area...
  this._MinOutputHeight = 200;

  // If an Apple iPod iPhone/iTouch is detected, set output area to 400 pixels...
  if (this._UserAgent.indexOf('ipod') != -1) {
    this._MinOutputHeight = 400;
  }

  // If a BlackBerry PDA is detected, set output area to 400 pixels...
  if (this._UserAgent.indexOf('blackberry') != -1) {
    this._MinOutputHeight = 400;
  }

  // Set default input area height...
  this._InputAreaHeight = 180;

  // Button area base width: '130px'
  this._ba_base_width = 130;

  // Button area calculated width
  this._ba_calc_width = 130;

}

/* NstConsole.setDOMReady()
 *
 * This function should only be called for Mozilla browsers.
 * It will indicate that the DOM is ready... */

NstConsole.setDOMReady = function() {
  NstConsole._DOMReady = true;
}

//
// Set an event to fire when the DOM content is loaded (ready)
// for Mozilla browsers...
if (document.addEventListener) {
  document.addEventListener("DOMContentLoaded", NstConsole.setDOMReady, false);
}

/* NstConsole.setInsertPoint(domNode)
 *
 * domNode - DOM node to append console to (if you don't want us to
 * automatically append to the bottom of the document). */

NstConsole.setInsertPoint = function(domNode) {

  NstConsole._InsertPoint = domNode;

}

/* NstConsole.getVersion()
 *
 *   Return the version of the NST Console as a string. */

NstConsole.getVersion = function() {

  return "1.0.0";

}

/* NstConsole.setInputAreaHeight(pixels)
 *
 * pixels - The desired height of the input area at the bottom in pixels. */

NstConsole.setInputAreaHeight = function(pixels) {

  NstConsole._InputAreaHeight = pixels;

}

/* NstConsole.getInputAreaHeight()
 *
 * Returns the desired height of the input area at the bottom in pixels. */

NstConsole.getInputAreaHeight = function() {

  return NstConsole._InputAreaHeight;

}

/* NstConsole.setMinOutputHeight(pixels)
 *
 *   Set the minimum height (in pixels) used for the console output area. */

NstConsole.setMinOutputHeight = function(pixels) {

  NstConsole._MinOutputHeight = pixels;

}

/* NstConsole.getMinOutputHeight()
 *
 *   Get the minimum height (in pixels) used for the console output area. */

NstConsole.getMinOutputHeight = function() {

  return NstConsole._MinOutputHeight;

}

/* NstConsole.setOneLineMode(BOOL)
 *
 *   Enable/disable one line input mode. */

NstConsole.setOneLineMode = function(enable) {

  NstConsole._OneLineMode = (enable == true);

  var iline = document.getElementById("nstConsoleOneLiner");
  var div1LnSep = document.getElementById("nstConsoleOneLinerSeparator");
  var mline = document.getElementById("nstConsoleInput");
  var br = document.getElementById("nstConsoleBotRight");
  var bot = document.getElementById("nstConsoleBot");

  if (iline && mline) {
    // one line mode...
    if (NstConsole._OneLineMode) {
      iline.style.display = 'block';
      div1LnSep.style.display = 'block';
      mline.style.display = 'none';
      br.style.left = (NstConsole._ba_calc_width + 2) + 'px';
      bot.style.backgroundColor = '#000030';
    } else {
      // multi-line mode...
      iline.style.display = 'none';
      div1LnSep.style.display = 'none';
      mline.style.display = 'block';
      br.style.left = (NstConsole._ba_calc_width + 7) + 'px';
      bot.style.backgroundColor = 'black';
    }

    // set cursor focus and scroll into view
    NstConsole.setFocusAndScrollTo();
  }
}

/* NstConsole.isOneLineMode()
 *
 *   Returns true if console is in "one line" mode. */

NstConsole.isOneLineMode = function() {

  return NstConsole._OneLineMode;

}

/* NstConsole.debugLevel(level)
 *
 *   Used internally to insert diagnostic output inline, as shown below:
 *
 *   if (NstConsole.debugLevel(2)) {
 *     NstConsole.echo("===== _SESSION INFO =====", false, "nstConsoleEvalOutput");
 *     NstConsole.var_dump(_SESSION);
 *   }
 * 
 *   Returns true if debug level is at least 1. */

NstConsole.debugLevel = function(level) {
  return (level <= NstConsole._DebugLevel);
}

/* NstConsole.getDebugLevel()
 *
 * Returns the current debug level. */

NstConsole.getDebugLevel = function() {
  return NstConsole._DebugLevel;
}

/* NstConsole.setDebugLevel(level)
 *
 * Sets the debug level verbosity.
 * 
 * level - New debug level (0 to turn off, higher numbers for more verbosity). */

NstConsole.setDebugLevel = function(level) {
  NstConsole._DebugLevel = level;
}


/* NstConsole.setLineLength(charCnt)
 *
 * Set the number of maximum number of characters to appear on each line
 * of output.
 *
 * charCnt - Values larger than 0 are explicit line lengths, 0 is
 * unlimited (no line breaks) and -1 is auto detect mode. */

NstConsole.setLineLength = function(ccnt) {

  if (ccnt >= 0) {
    NstConsole._LineLength = ccnt;
  } else if (ccnt == -1) {
    if (navigator.appName == "Microsoft Internet Explorer") {
      // Go ahead and let IE go to full width as well
      NstConsole._LineLength = 0;
    } else {
      NstConsole._LineLength = 0;
    }
  }

}

/* NstConsole.getLineLength()
 *
 * Get the number of maximum number of characters to appear on each line
 * of output.
 *
 * returns number of characters where values larger than 0 are
 * explicit line lengths, 0 is unlimited (no line breaks) and -1 is
 * auto detect mode. */

NstConsole.getLineLength = function() {

  return NstConsole._LineLength;

}

/* NstConsole.createHeaderArea()
 *
 * This stub function does nothing. You may replace this function if you would
 * like to add a header section to the top of the console.
 *
 * Return DOM node if you want to add something to the header. */

NstConsole.createHeaderArea = function() {
  return null;
}

/* NstConsole.createFooterArea()
 *
 * This stub function does nothing. You may replace this function if you would
 * like to update the "button area" of the console.
 *
 * Return DOM node if you want to add something to the footer area of the
 * console (or null if not). */

NstConsole.createFooterArea = function() {
  return null;
}

/* NstConsole.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. */

NstConsole.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 ];
    }
  }

  return size;    
}

/* NstConsole.removeConsole()
 *
 * Removes the console from the page. */

NstConsole.removeConsole = function() {

  var console = NstConsole._Console;

  if (console != null) {
    console.parentNode.removeChild(console);
    NstConsole._Console = null;
    NstConsole._OutputDiv = null;
    NstConsole._InputDiv = null;
  }

}

/* NstConsole.openConsole()
 *
 * Opens console (at end of page) - if not shown. */

NstConsole.openConsole = function() {

  var create = NstConsole._Console == null;

  if (create) {

    var console = NstConsole._Console = document.createElement("div");
    console.className = console.id = "nstConsole";

    var headerArea = NstConsole.createHeaderArea();
    if (headerArea) {
      console.appendChild(headerArea);
    }

    var top = document.createElement("div");
    top.className = top.id = "nstConsoleTop";
    console.appendChild(top);

    var bot = document.createElement("div");
    bot.className = bot.id = "nstConsoleBot";
    console.appendChild(bot);

    var outputDiv = document.createElement("div");
    NstConsole._OutputDiv = outputDiv;
    outputDiv.className = outputDiv.id = "nstConsoleOutput";
    top.appendChild(outputDiv);

    // Create the bottom left/right areas
    var botLeft = document.createElement("div");
    botLeft.className = botLeft.id = "nstConsoleBotLeft";
    botLeft.style.width = NstConsole._ba_calc_width + 'px';
    bot.appendChild(botLeft);

    var botRight = document.createElement("div");
    botRight.className = botRight.id = "nstConsoleBotRight";
    botRight.style.left = (NstConsole._ba_calc_width + 7) + 'px';
    bot.appendChild(botRight);
    
    /* Create input area. */
    var inputDiv = document.createElement("div");
    NstConsole._InputDiv = inputDiv;
    inputDiv.className = "nstConsoleInputDiv";
    botRight.appendChild(inputDiv);

    // One line mode
    var iline = document.createElement("input");
    iline.type = "text";
    iline.size = "132";
    iline.id = "nstConsoleOneLiner";
    iline.onkeydown = function(event) {
      NstConsole.checkKeyCodes(this, event);
    }

    iline.style.display = (NstConsole._OneLineMode ? 'block' : 'none');
    inputDiv.appendChild(iline);

    //
    // Create a <div> for one liner input separator...
    var div1LnSep = document.createElement("div");
    div1LnSep.id = "nstConsoleOneLinerSeparator";
    div1LnSep.style.display = (NstConsole._OneLineMode ? 'block' : 'none');
    inputDiv.appendChild(div1LnSep);

    // Multiline mode - use a <textarea>
    var ta = document.createElement("textarea");
    ta.rows = 2;
    ta.cols = 132;
    ta.id = "nstConsoleInput";
    ta.style.display = (NstConsole._OneLineMode ? 'none' : 'block');

    ta.onkeydown = function(event) {
      NstConsole.checkKeyCodes(this, event);
    }
    inputDiv.appendChild(ta);

    //
    // Initialize decoration tweaks...
    if (NstConsole._OneLineMode) {
      botRight.style.left = (NstConsole._ba_calc_width + 2) + 'px';
      bot.style.backgroundColor = '#000030';
    } else {
      botRight.style.left = (NstConsole._ba_calc_width + 7) + 'px';
      bot.style.backgroundColor = 'black';
    }

    var center = NstConsole._ButtonArea = document.createElement("div");
    center.align = "left";
    center.className = center.id = "nstConsoleButtons";
    center.style.width = NstConsole._ba_calc_width + 'px';
    botLeft.appendChild(center);

    /* Add a row of "evaluation" related buttons. */

    var btable = document.createElement("table");
    btable.className = btable.id = "nstConsoleMiniButtons";
    btable.cellPadding = "0";
    btable.cellSpacing = "2px";
    center.appendChild(btable);

    var btbody = document.createElement("tbody");
    btable.appendChild(btbody);

    var btr = document.createElement("tr");
    btbody.appendChild(btr);

    var btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var eval = document.createElement("a");
    eval.href = "javascript:NstConsole.evalUserInput(NstConsole.getActiveInput().value);";
    eval.className = "nstConsoleButton";
    eval.appendChild(document.createTextNode("Run"));
    btd.appendChild(eval);

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var ceval = document.createElement("a");
    ceval.href = "javascript:NstConsole.clear(false, true);NstConsole.evalUserInput(NstConsole.getActiveInput().value);";
    ceval.className = "nstConsoleButton";
    ceval.appendChild(document.createTextNode("CAR"));
    btd.appendChild(ceval);

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var tol = document.createElement("a");
    tol.href = "javascript:NstConsole.setOneLineMode(!NstConsole.isOneLineMode());NstConsole.scrollTo();";
    tol.className = "nstConsoleButton";
    tol.appendChild(document.createTextNode("TOL"));
    btd.appendChild(tol);

    /* Add a row of "clear" related buttons. */

    btr = document.createElement("tr");
    btbody.appendChild(btr);

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var oclear = document.createElement("a");
    oclear.href = "javascript:NstConsole.clear(false, true);NstConsole.setFocusAndScrollTo();";
    oclear.className = "nstConsoleButton";
    oclear.appendChild(document.createTextNode("CO"));
    btd.appendChild(oclear);

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var iclear = document.createElement("a");
    iclear.href = "javascript:NstConsole.clear(true, false);NstConsole.setFocusAndScrollTo();";
    iclear.className = "nstConsoleButton";
    iclear.appendChild(document.createTextNode("CI"));
    btd.appendChild(iclear);

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var bclear = document.createElement("a");
    bclear.href = "javascript:NstConsole.clear(true, true);NstConsole.setFocusAndScrollTo();";
    bclear.className = "nstConsoleButton";
    bclear.appendChild(document.createTextNode("CB"));
    btd.appendChild(bclear);

    /* Add a row for "PRT", "SO" and "GID". */

    btr = document.createElement("tr");
    btbody.appendChild(btr);

    // Add "Print" button.

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var iecho = document.createElement("a");
    iecho.href = "javascript:NstConsole.insert('NstConsole.echo(', ');');";
    iecho.className = "nstConsoleButton";
    iecho.appendChild(document.createTextNode("Prt"));
    btd.appendChild(iecho);

    // Add "Show Object" button

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var ivardump = document.createElement("a");
    ivardump.href = "javascript:NstConsole.insert('NstConsole.var_dump(', ');');";
    ivardump.className = "nstConsoleButton";
    ivardump.appendChild(document.createTextNode("SO"));
    btd.appendChild(ivardump);

    // Add "GID" button

    btd = document.createElement("td");
    btd.style.width = "33%";
    btr.appendChild(btd);

    var gid = document.createElement("a");
    gid.href = "javascript:NstConsole.gid();";
    gid.className = "nstConsoleButton";
    gid.appendChild(document.createTextNode("GID"));
    btd.appendChild(gid);

    // Add "Debug Level" button

    btr = document.createElement("tr");
    btbody.appendChild(btr);

    btd = document.createElement("td");
    btd.style.width = "100%";
    btd.colSpan = "3";
    btr.appendChild(btd);

    var idebug = document.createElement("a");
    idebug.href = "javascript:NstConsole.insert('NstConsole.setDebugLevel(', ');');";
    idebug.className = "nstConsoleButton";
    idebug.appendChild(document.createTextNode("Debug Level"));
    btd.appendChild(idebug);

    // Add "Resize" button.
    btr = document.createElement("tr");
    btbody.appendChild(btr);

    btd = document.createElement("td");
    btd.style.width = "100%";
    btd.colSpan = "3";
    btr.appendChild(btd);

    var iresize = document.createElement("a");
    iresize.href = "javascript:NstConsole.setFocusAndScrollTo();";
    iresize.className = "nstConsoleButton";
    iresize.appendChild(document.createTextNode("Resize Console"));
    btd.appendChild(iresize);

    // Add tooltips (if DOM tooltips are available)
    if (window.domTT_tooltips) {
      // Run button...
      eval.onmouseover = function(event) {
	var tt = "<span  class=\"ttAction\">Evaluate</span> and <u><span  class=\"ttAction\">Run</span></u> the "
          + "<span  class=\"ttEmphasis\">JavaScript</span> code "
          + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
          + "<span  class=\"ttValue\">Enter</span>&#41;. "
          + "<span  class=\"ttAction\">All</span> <span  class=\"ttEmphasis\">Results</span> "
          + "and <span  class=\"ttEmphasis\">Errors</span> "
          + "will be <span  class=\"ttAction\">Displayed</span> in the "
          + "&quot;<span  class=\"ttValue\">Output Area</span>&quot; above." 

          + "<br /><div class=\"line1px\"><br />"
          + "<span  class=\"ttAction\">Operational Notes</span>:</div>"

          + "&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">1</span>&#41;"
          + "&nbsp;&nbsp;Each line of output will be "
          + "<span  class=\"ttAction\">Preceded</span> by a <span  class=\"ttEmphasis\">Line Number</span>."

          + "<br />&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">2</span>&#41;"
          + "&nbsp;&nbsp;Both <span  class=\"ttEmphasis\">Line Numbers</span> "
          + "and the <span  class=\"ttEmphasis\">Line Continuation Character</span>: "
          + "&quot;<span  class=\"ttCaution\">/ </span>&quot; will be "
          + "<span  class=\"ttAction\">Displayed</span> in<br />"
          + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
          + "\'<span  class=\"ttCaution\">Yellow</span>\' text."

          + "<br />&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">3</span>&#41;"
          + "&nbsp;&nbsp;<span  class=\"ttAction\">First</span>, each line of the "
          + "&quot;<span  class=\"ttValue\">Input Area</span>&quot; will be <span  class=\"ttAction\">Echoed</span> "
          + "and <span  class=\"ttAction\">Displayed</span> in "
          + "&#39;<span  class=\"ttNormal\">White</span>&#39; text."

          + "<br />&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">4</span>&#41;"
          + "&nbsp;&nbsp;<span  class=\"ttAction\">Next</span>, the "
          + "&quot;<span  class=\"ttValue\">Input Area</span>&quot; will then be "
          + "<span  class=\"ttAction\">Evaluated</span> as if it was a "
          + "&quot;<span  class=\"ttValue\">JavaScript Source File</span>&quot;."

          + "<br />&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">5</span>&#41;"
          + "&nbsp;&nbsp;Any \'<span  class=\"ttEmphasis\">NstConsole.echo();</span>\' or "
          + "\'<span  class=\"ttEmphasis\">NstConsole.var_dump();</span>\' statement "
          + "<span  class=\"ttEmphasis\">Results</span> will be<br />"
          + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
          + "<span  class=\"ttAction\">Displayed</span> in "
          + "\'<span  class=\"ttEmphasis\">Green</span>\' text."

          + "<br />&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">6</span>&#41;"
          + "&nbsp;&nbsp;Any &quot;<span  class=\"ttValue\">JavaScript Error</span>&quot; that "
          + "may occur will be <span  class=\"ttAction\">Displayed</span> "
          + "in \'<span  class=\"ttWarning\">Red</span>\' text."

          + "<br />&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">7</span>&#41;"
          + "&nbsp;&nbsp;Any "
          + "&quot;<span  class=\"ttValue\">JavaScript Error</span>&quot; will "
          + "<span  class=\"ttAction\">Terminate</span> subsequent "
          + "&quot;<span  class=\"ttValue\">JavaScript Code</span>&quot; "
          + "<span  class=\"ttAction\">Evaluation</span>."

          + "<br />&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttAction\">8</span>&#41;"
          + "&nbsp;&nbsp;<span  class=\"ttAction\">Finally</span>, a "
          + "\'<span  class=\"ttEmphasis\">NstConsole.var_dump();</span>\' statement "
          + "will be automatically <span  class=\"ttAction\">Applied</span> to any<br />"
          + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
          + "<span  class=\"ttEmphasis\">Results</span> "
          + "returned from the <span  class=\"ttAction\">Evaluation</span>. "

          + "<br /><div class=\"line1px\"><br />"
          + "<span  class=\"ttAction\">Shortcut Usage</span>:</div>"

          + "One can <span  class=\"ttAction\">Enter</span> a <span  class=\"ttEmphasis\">Short</span> "
          + "number of &quot;<span  class=\"ttValue\">JavaScript Expressions</span>&quot; "
          + "to obtain quick <span  class=\"ttEmphasis\">Results</span> "
          + "by taking <span  class=\"ttAction\">Advantage</span> "
          + "of <span  class=\"ttEmphasis\">Operational Note</span>: "
          + "&quot;<span  class=\"ttValue\">8</span>&quot; without the use of the "
          + "\'<span  class=\"ttEmphasis\">NstConsole.echo();</span>\' or the "
          + "\'<span  class=\"ttEmphasis\">NstConsole.var_dump();</span>\' statement."

          + "<div class=\"verticalGapSmall\"></div>"
          + "<u><span  class=\"ttAction\">Example</span>:</u><br />"
          + "<span style=\"font-family: monospace; font-weight: normal;\">"
          + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">function sum(x,y) {</span><br />"
          + "&nbsp;&nbsp;&nbsp;&nbsp;<span  class=\"ttEmphasis\">return x + y;</span>"
          + "<br />&nbsp;&nbsp;<span  class=\"ttEmphasis\">}</span><br />"
          + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">sum(10,20);</span>"
          + "</span>"
          + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
          + "<span  class=\"ttValue\">Enter</span>&#62;";

	domTT_activate(this, event, 'content', tt, 'width', 580,
                       'x', NstConsole.getViewportX() + 100,
                       'y', NstConsole.getViewportY() + 5);
      }

      // Clear and Run button...
      ceval.onmouseover = function(event) {
	var tt = "First <u><span  class=\"ttAction\">C</span></u><span  class=\"ttAction\">lear</span> the "
          + "&quot;<span  class=\"ttValue\">Output Area</span>&quot; "
          + "<u><span  class=\"ttAction\">A</span></u><span  class=\"ttAction\">nd</span> "
	  + "then <span  class=\"ttAction\">Evaluate</span> and "
          + "<u><span  class=\"ttAction\">R</span></u><span  class=\"ttAction\">un</span> the "
          + "<span  class=\"ttEmphasis\">JavaScript</span> code. "
          + "<span  class=\"ttAction\">All</span> <span  class=\"ttEmphasis\">Results</span> "
          + "and <span  class=\"ttEmphasis\">Errors</span> "
          + "will be <span  class=\"ttAction\">Displayed</span> in the "
          + "&quot;<span  class=\"ttValue\">Output Area</span>&quot; above."; 

	domTT_activate(this, event, 'content', tt, 'width', 420);
      }

      // Toggle One-Liner Input Area...
      tol.onmouseover = function(event) {
	var tt = "<u><span  class=\"ttAction\">T</span></u><span  class=\"ttAction\">oggles</span> "
          + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
          + "<span  class=\"ttValue\">t</span>&#41; whether the "
          + "&quot;<u><span  class=\"ttValue\">O</span></u><span  class=\"ttValue\">ne-</span>"
          + "<u><span  class=\"ttValue\">L</span></u><span  class=\"ttValue\">iner Input Area</span>&quot; is "
          + "<span  class=\"ttEmphasis\">Visible</span>. When "
          + "present, one may <span  class=\"ttAction\">Type</span> "
          + "<span  class=\"ttEmphasis\">JavaScript Commands</span> within the "
          + "&quot;<span  class=\"ttValue\">One-Liner Input Area</span>&quot; and then "
          + "<span  class=\"ttAction\">Execute</span> the commands by "
          + "<span  class=\"ttAction\">Pressing</span> the &quot;<span  class=\"ttValue\">Enter</span>&quot; key."
          + "<div class=\"verticalGap\"></div><u><span  class=\"ttAction\">Example</span>:</u>&nbsp;&nbsp;"
          + "<div class=\"verticalGapSmall\"></div>"
          + "<span style=\"font-family: monospace; font-weight: normal;\">"
          + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">3 + 4.5;</span>"
          + "</span>&#60;<span  class=\"ttValue\">Enter</span>&#62;";

	domTT_activate(this, event, 'content', tt, 'width', 480);
      }

      // Clear Output...
      oclear.onmouseover = function(event) {
	var tt = "<u><span  class=\"ttAction\">C</span></u><span  class=\"ttAction\">lear</span> the "
          + "&quot;<u><span  class=\"ttValue\">O</span></u><span  class=\"ttValue\">utput Area</span>&quot; "
          + "above "
          + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
          + "<span  class=\"ttValue\">c</span>&#41;.";

	domTT_activate(this, event, 'content', tt, 'width', 290);
      }

      // Clear Input...
      iclear.onmouseover = function(event) {
	var tt = "<u><span  class=\"ttAction\">C</span></u><span  class=\"ttAction\">lear</span> the "
          + "&quot;<u><span  class=\"ttValue\">I</span></u><span  class=\"ttValue\">nput Area</span>&quot; "
          + "to the right "
          + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
          + "<span  class=\"ttValue\">i</span>&#41;.";

	domTT_activate(this, event, 'content', tt, 'width', 310);
      }

      // Clear Both I/O regions...
      bclear.onmouseover = function(event) {
	var tt = "<u><span  class=\"ttAction\">C</span></u><span  class=\"ttAction\">lear</span> "
          + "<u><span  class=\"ttAction\">B</span></u><span  class=\"ttAction\">oth</span> the "
          + "&quot;<span  class=\"ttValue\">Output Area</span>&quot; above and the "
          + "&quot;<span  class=\"ttValue\">Input Area</span>&quot; to the right "
          + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
          + "<span  class=\"ttValue\">b</span>&#41;.";

	domTT_activate(this, event, 'content', tt, 'width', 340);
      }

      // echo button...
      iecho.onmouseover = function(event) {
	  var tt = "<span  class=\"ttAction\">Insert</span> an "
            + "\'<span  class=\"ttEmphasis\">NstConsole.echo();</span>\' statement in "
            + "the &quot;<span  class=\"ttValue\">Input Area</span>&quot; "
            + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">e</span>&#41;. "
            + "This \'<span  class=\"ttEmphasis\">NST JavaScript Console Method</span>\' "
            + "is used to "
            + "<u><span  class=\"ttAction\">Pr</span></u><span  class=\"ttAction\">in</span><u><span  class=\"ttAction\">t</span></u>"
            + " information to the &quot;<span  class=\"ttValue\">Output Area</span>&quot;."
            + "<div class=\"verticalGap\"></div><u><span  class=\"ttAction\">Hint</span>:</u>&nbsp;&nbsp;"
            + "First <span  class=\"ttAction\">Highlight</span> the &quot;<span  class=\"ttValue\">Text</span>&quot; "
            + "to <span  class=\"ttEmphasis\">Display</span> and then <span  class=\"ttAction\">Click</span> on "
            + "the &quot;<span  class=\"ttValue\">Print</span>&quot; button or <span  class=\"ttAction\">Use</span> "
            + "the <span  class=\"ttEmphasis\">Keyboard Shortcut</span>: "
            + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">e</span>&#62;."
            + "<div class=\"verticalGap\"></div><u><span  class=\"ttAction\">Example</span>:</u>"
            + "<span style=\"font-family: monospace; font-weight: normal;\">"
            +" <div class=\"verticalGapSmall\"></div>"
            + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">NstConsole.echo&#40;</span>"
            + "&quot;<span  class=\"ttValue\">Hello World!</span>&quot;"
            + "<span  class=\"ttEmphasis\">&#41;;</span></span>"
            + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">Enter</span>&#62;";


	domTT_activate(this, event, 'content', tt, 'width', 460);
      }

      // var_dump button...
      ivardump.onmouseover = function(event) {
	  var tt = "<span  class=\"ttAction\">Insert</span> an "
            + "\'<span  class=\"ttEmphasis\">NstConsole.var_dump();</span>\' statement in "
            + "the &quot;<span  class=\"ttValue\">Input Area</span>&quot; "
            + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">v</span>&#41;. "
            + "This \'<span  class=\"ttEmphasis\">NST JavaScript Console Method</span>\' "
            + "is used to <u><span  class=\"ttAction\">S</span></u><span  class=\"ttAction\">how</span> "
            + "<span  class=\"ttAction\">Detailed</span> information about a particular "
            + "<span  class=\"ttEmphasis\">JavaScript</span> "
            + "<u><span  class=\"ttAction\">O</span></u><span  class=\"ttAction\">bject</span> in the "
            + "&quot;<span  class=\"ttValue\">Output Area</span>&quot;."
            + "<div class=\"verticalGap\"></div><u><span  class=\"ttAction\">Hint</span>:</u>&nbsp;&nbsp;"
            + "First <span  class=\"ttAction\">Highlight</span> the &quot;<span  class=\"ttValue\">Object</span>&quot; "
            + "to <span  class=\"ttEmphasis\">Display</span> and then <span  class=\"ttAction\">Click</span> on "
            + "the &quot;<span  class=\"ttValue\">Show Object</span>&quot; button or "
            + "<span  class=\"ttAction\">Use</span> the <span  class=\"ttEmphasis\">Keyboard Shortcut</span>: "
            + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">v</span>&#62;."
            + "<div class=\"verticalGap\"></div><u><span  class=\"ttAction\">Examples</span>:</u>"
            + "<span style=\"font-family: monospace; font-weight: normal;\">"
            +" <div class=\"verticalGapSmall\"></div>"
            + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">NstConsole.var_dump&#40;</span>"
            + "<span  class=\"ttValue\">window.location</span><span  class=\"ttEmphasis\">&#41;;</span></span>"
            + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">Enter</span>&#62;"
            + "<span style=\"font-family: monospace; font-weight: normal;\">"
            +" <div class=\"verticalGapSmall\"></div>"
            + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">NstConsole.var_dump&#40;</span>"
            + "<span  class=\"ttValue\">window.navigator</span><span  class=\"ttEmphasis\">&#41;;</span></span>"
            + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">Enter</span>&#62;";

	domTT_activate(this, event, 'content', tt, 'width', 500);
      }

      // GID button...
      gid.onmouseover = function(event) {
	  var tt = "<span  class=\"ttAction\">Insert</span> an "
            + "\'<span  class=\"ttEmphasis\">var eXXX = document.getElementById();</span>\' statement in "
            + "the &quot;<span  class=\"ttValue\">Input Area</span>&quot; "
            + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">g</span>&#41;. "
            + "This \'<span  class=\"ttEmphasis\">NST JavaScript Console Method</span>\' "
            + "is used to <u><span  class=\"ttAction\">G</span></u><span  class=\"ttAction\">et</span> "
            + "a particular <span  class=\"ttAction\">entity</span> from the document for a given <u><span  class=\"ttAction\">ID</span></u>."
            + "<div class=\"verticalGap\"></div><u><span  class=\"ttAction\">Hint</span>:</u>&nbsp;&nbsp;"
            + "First <span  class=\"ttAction\">Highlight</span> the &quot;<span  class=\"ttValue\">ID</span>&quot; "
            + "to <span  class=\"ttEmphasis\">Get</span> and then <span  class=\"ttAction\">Click</span> on "
            + "the &quot;<span  class=\"ttValue\">GID</span>&quot; button or "
            + "<span  class=\"ttAction\">Use</span> the <span  class=\"ttEmphasis\">Keyboard Shortcut</span>: "
            + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">g</span>&#62;."
            + "<div class=\"verticalGap\"></div><u><span  class=\"ttAction\">Example</span>:</u>"
            +" <div class=\"verticalGapSmall\"></div>"
            + "<span style=\"font-family: monospace; font-weight: normal;\">"
            + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">var e0 = document.getElementById&#40;\'</span>"
            + "<span  class=\"ttValue\">nstConsoleOneLiner</span><span  class=\"ttEmphasis\">\'&#41;;</span></span><br/>"
            + "<span style=\"font-family: monospace; font-weight: normal;\">"
            + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">e0.value = \'var r = 2.5; Math.PI * r * r;\';</span></span>"
            + "&#60;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">Enter</span>&#62;";

	domTT_activate(this, event, 'content', tt, 'width', 500);
      }

      // debug level...
      idebug.onmouseover = function(event) {
	  var tt = "<span  class=\"ttAction\">Insert</span> an "
            + "\'<span  class=\"ttEmphasis\">NstConsole.setDebugLevel();</span>\' "
            + "statement in the &quot;<span  class=\"ttValue\">Input Area</span>&quot; "
            + "&#40;<span  class=\"ttValue\">Ctrl</span>+<span  class=\"ttValue\">Alt</span>+"
            + "<span  class=\"ttValue\">d</span>&#41;. "
            + "This \'<span  class=\"ttEmphasis\">NST JavaScript Console Method</span>\' "
            + "is used to <span  class=\"ttAction\">Control</span> the <span  class=\"ttAction\">Verbosity</span> "
            + "of <u><span  class=\"ttAction\">Debug</span></u> <u><span  class=\"ttAction\">Output</span></u> "
            + "statements <span  class=\"ttAction\">Embedded</span> in the "
            + "<span  class=\"ttEmphasis\">JavaScript</span> code on this page. "
            + "<span  class=\"ttAction\">Use</span> a value of "
            + "&quot;<span  class=\"ttValue\">Zero (0)</span>&quot; to <span  class=\"ttAction\">Disable</span> "
            + "and &quot;<span  class=\"ttValue\">Positive Integer</span>&quot; "
            + "values for the <span  class=\"ttAction\">Desired</span> "
            + "\'<span  class=\"ttEmphasis\">Debug Level</span>\' output."
            + "<div class=\"verticalGap\"></div><span  class=\"ttAction\">Embed</span> the "
            + "<span  class=\"ttEmphasis\">NstConsole.debugLevel&#40;</span>"
            + "&lt;<span  class=\"ttValue\">level</span>&gt;<span  class=\"ttEmphasis\">&#41;</span> method "
            + "within your <span  class=\"ttEmphasis\">JavaScript</span> code as the "
            + "<span  class=\"ttAction\">Control Logic</span> to determine if "
            + "&quot;<span  class=\"ttValue\">Debug Output</span>&quot; "
            + "is <span  class=\"ttAction\">Allowed</span> or <span  class=\"ttAction\">Not</span>. If the "
            + "\'<span  class=\"ttEmphasis\">NST JavaScript Console</span>\' "
            + "has <span  class=\"ttAction\">Not</span> "
            + "been <i>previously</i> <span  class=\"ttAction\">Opened</span>, it will "
            + "automatically <span  class=\"ttAction\">Open</span> at the "
            + "\'<span  class=\"ttEmphasis\">Bottom</span>\' of the page if "
            + "&quot;<span  class=\"ttValue\">Output</span>&quot; is <span  class=\"ttAction\">Sent</span> to the "
            + "\'<span  class=\"ttEmphasis\">Console</span>\'."
            + "<div class=\"line1px\"><br />"
            + "<span  class=\"ttAction\">Example</span>:&nbsp;&nbsp;"
            + "<span  class=\"ttAction\">Set</span> the "
            + "\'<span  class=\"ttEmphasis\">Debug Output Level</span>\' "
            + "to &quot;<span  class=\"ttValue\">5</span>&quot; or &quot;<span  class=\"ttValue\">Less</span>&quot;</div>"
            + "<span style=\"font-family: monospace; font-weight: normal;\">"
            + "<div class=\"verticalGapSmall\"></div>"
            + "&nbsp;&nbsp;<span  class=\"ttEmphasis\">NstConsole.setDebugLevel&#40;</span>"
            + "<span  class=\"ttValue\">5</span><span  class=\"ttEmphasis\">&#41;;</span><br />"
            + "&nbsp;&nbsp;x = 3;<br />"
            + "&nbsp;&nbsp;//<br />"
            + "&nbsp;&nbsp;// <span  class=\"ttAction\">Output the following debug message to the NST JavaScript</span><br />"
            + "&nbsp;&nbsp;// <span  class=\"ttAction\">Console if the current debug level is set to:</span> &quot;<span  class=\"ttValue\">4</span>&quot; <span  class=\"ttAction\">or greater...</span><br />"
            + "&nbsp;&nbsp;if &#40;"
            + "<span  class=\"ttEmphasis\">NstConsole.debugLevel&#40;</span>"
            + "<span  class=\"ttValue\">4</span><span  class=\"ttEmphasis\">&#41;</span>&#41; {<br />&nbsp;&nbsp;"
            + "&nbsp;&nbsp;NstConsole.echo(\"The value of x: \" + x);<br />"
            + "&nbsp;&nbsp;}</span>";

	domTT_activate(this, event, 'content', tt, 'width', 600);
      }

      // Resize JS Console...
      iresize.onmouseover = function(event) {
        var tt = "<span  class=\"ttAction\">Resize</span> the "
          + "\'<span  class=\"ttEmphasis\">NST JavaScript Console</span>\' "
          + "to <span  class=\"ttAction\">Fit</span> within the <i>entire</i> "
          + "&quot;<span  class=\"ttValue\">Window Frame</span>&quot; of the "
          + "&quot;<span  class=\"ttValue\">Browser</span>&quot;. <span  class=\"ttAction\">Typically</span>, "
          + "this \'<span  class=\"ttEmphasis\">Action</span>\' may be needed after a "
          + "<span  class=\"ttAction\">Resize</span> adjustment has been made to the "
          + "&quot;<span  class=\"ttValue\">Browser\'s Window Frame</span>&quot;.";

        domTT_activate(this, event, 'content', tt, 'width', 400);
      }
    }


    // Allow for customization of button area
    var footerArea = NstConsole.createFooterArea();
    if (footerArea) {
      /* If given a footer area, create following:

         <div id="nstConsoleFooter" class="nstConsoleFooter">
            FOOTER_AREA
         <div>
      */
      var footer = NstConsole._Footer = document.createElement("div");
      footer.id = footer.className = "nstConsoleFooter";
      footer.appendChild(footerArea);

      botLeft.appendChild(footer);
    }

    // Add console to document
    if (NstConsole._InsertPoint) {
      NstConsole._InsertPoint.appendChild(console);
    } else {
      document.body.appendChild(console);
    }

    // Set counter to 0
    NstConsole._DebugCounter = 0;

    // Append any cached echos and clear the cache
    var ecache = NstConsole._EchoCached;
    NstConsole._EchoCached = [];
    for (var i = 0; i < ecache.length; i++) {
      NstConsole.echo(ecache[i]._Text, false, ecache[i]._ClassName);
    }
  }

  // Set display attribute to 'block'
  NstConsole._Console.style.display = 'block';

  // If just created and font control is available
  if (create && window.FontControl) {

  NstDom.addToolTip('fontIncreaseIJC', NstDom.ttNote("Increase")
                    + " the " + NstDom.ttEmphasis("Font Size", true)
                    + " of the "
                    + NstDom.ttValue("Input", true) + " area...",
                    310);

  NstDom.addToolTip('fontDecreaseIJC', NstDom.ttNote("Decrease")
                    + " the " + NstDom.ttEmphasis("Font Size", true)
                    + " of the "
                    + NstDom.ttValue("Input", true) + " area...",
                    310);

  NstDom.addToolTip('fontRestoreIJC', NstDom.ttNote("Restore")
                    + " the " + NstDom.ttEmphasis("Font Size", true)
                    + " of the "
                    + NstDom.ttValue("Input", true) + " area...",
                    300);

    var infc = new FontControl(document.getElementById("nstConsoleInput"), {
      ttIncId: 'fontIncreaseIJC',
      ttDecId: 'fontDecreaseIJC',
      ttResId: 'fontRestoreIJC',
      buttonGrids: FontControl.BUTTON_GRID_UR,

      buttons:
        FontControl.RESTORE_BUTTON | FontControl.INCREASE_BUTTON | FontControl.DECREASE_BUTTON
     });

    var outfc = new FontControl(document.getElementById("nstConsoleOutput"), {
      buttonGrids: FontControl.BUTTON_GRID_UR,

      buttons:
        FontControl.RESTORE_BUTTON | FontControl.INCREASE_BUTTON | FontControl.DECREASE_BUTTON
     });
  }

  // set size, cursor focus and scroll window
  NstConsole.setFocusAndScrollTo();

}

/* NstConsole.resize()
 *
 *   Attempts to resize the NST JavaScript console based on the current window
 *   size. */

NstConsole.resize = function() {

  var console = NstConsole._Console;

  // Get size of available output
  var vsize = NstConsole.getViewportSize();

  // Adjust height of output area (so total height is close to full)
  if (console.offsetHeight && vsize) {
    var heightButtons = NstConsole._ButtonArea.offsetHeight;

    // If custom footer, include its height in button area calculation
    if (NstConsole._Footer) {
      heightButtons += NstConsole._Footer.offsetHeight;
    }

    var heightInput = NstConsole._InputAreaHeight;

    // Choose the larger of the two bottom panel heights
    var heightIarea = heightButtons;

    if (NstConsole.debugLevel(9)) {
      NstConsole.echo("Resize bottom, desired height: " + heightInput
                      + "  minimum height: "  + heightButtons);
    }

    if (heightInput > heightButtons) {
      heightIarea = heightInput;
    } else {
      // Enlarge
      heightInput = heightButtons;
    }
    document.getElementById("nstConsoleInput").style.height = heightInput + "px";


    // Set height of bottom areas
    var bot = document.getElementById("nstConsoleBot");
    var br = document.getElementById("nstConsoleBotRight");
    var bl = document.getElementById("nstConsoleBotLeft");

    hbot = heightIarea + "px";
    bot.style.height = hbot;
    br.style.height = hbot;
    bl.style.height = hbot;

    // Backoff height for any necessary fixed "tweaks"
    var h = vsize[1] - NstConsole._OutputHeightTweak;

    // Take off height of area at bottom
    h -= heightIarea;

    //
    // force output area height for small view areas... 
    if (h < NstConsole._MinOutputHeight) {
      h = NstConsole._MinOutputHeight;
    }

    // Set height of the output area
    var top = document.getElementById("nstConsoleTop");
    top.style.height = h;
    NstConsole._OutputDiv.style.height = h + "px";
  }

}

/* NstConsole.scrollTo()
 *
 *   If the console is visible, this method attempts to scroll
 *   the current page such that the bottom of the console is
 *   aligned with the bottom of the viewport (so if any veritical
 *   scrolling is required, it will be required to view the top
 *   portion of the console). */

NstConsole.scrollTo = function() {
  if (NstConsole._Console) {

    // if relative model
    if (NstConsole._OutputDiv.offsetTop == 0) {
      // In relative mode, scroll to top of console PLUS the offset
      // to the top of the output area
      var top = document.getElementById("nstConsoleTop");
      window.scrollTo(0, NstConsole._Console.offsetTop + top.offsetTop);
    } else {
      // In absolute positioning mode we can scroll directly to output
      window.scrollTo(0, NstConsole._OutputDiv.offsetTop);
    }

  }

}

/* NstConsole.resizeAndScrollTo()
 *
 *   Attempts to resize the NST JavaScript console based on the current window
 *   size and scroll it into view (only if it's currently shown). */

NstConsole.resizeAndScrollTo = function() {

  if (NstConsole.isVisible()) {
    NstConsole.resize();
    NstConsole.scrollTo();
  }

}

/* NstConsole.isVisible()
 *
 * Returns true/false indicating whether the console is currently shown. */

NstConsole.isVisible = function() {
  return NstConsole._Console &&
    (NstConsole._Console.style.display != 'none');
}

/* NstConsole.setFocus()
 *
 * Sets the cursor focus to current input window. */

NstConsole.setFocus = function() {
  var iline = document.getElementById("nstConsoleOneLiner");
  var mline = document.getElementById("nstConsoleInput");
  if (NstConsole._OneLineMode) {
    iline.focus();
  } else {
    mline.focus();
  }
}

/* NstConsole.setFocusAndScrollTo()
 *
 * Sets the cursor focus to current input window and scrolls to position. */

NstConsole.setFocusAndScrollTo = function() {
  // Make sure window size is OK
  NstConsole.resize();

  // Set input focus
  NstConsole.setFocus();

  // Reset window position
  NstConsole.scrollTo();
}


/* NstConsole.toggleConsole()
 *
 * Opens/closes a JavaScript "console area" at the top of the page. */

NstConsole.toggleConsole = function() {

  if (NstConsole._Console == null) {

    // Not created yet, open it up
    this.openConsole();

    // Empirical: Added a short settling time period for IE
    // after body vertical scroll is enabled for proper
    // viewport calculations...
    var timerID = setTimeout('NstConsole.setFocusAndScrollTo()',10);

  } else {
    // Otherwise, just toggle the display of the console area
    if (NstConsole._Console.style.display == 'none') {

    // show console...
      NstConsole._Console.style.display = 'block';

    // Empirical: Added a short settling time period for IE
    // after body vertical scroll is enabled for proper
    // viewport calculations...
      var timerID = setTimeout('NstConsole.setFocusAndScrollTo()',10);

    } else {

    //  hide console...
      NstConsole._Console.style.display = 'none';
    }
  }
}

/* NstConsole.echo([text[, clear][, cname]])
 *
 * Increments and displays a internal counter and optional "text" at
 * top of page.
 *
 * text - Optional text to include next to counter.
 *
 * clear - Pass true if you want current output area cleared.
 * 
 * cname - Optional class name for <pre> area (defaults to
 * "nstConsoleEchoOutput" if omitted. */

NstConsole.echo = function(text, clear, cname) {
    var node = NstConsole._OutputDiv;

    // Get output text to appear next to each line number
    var otext = "";
    if (typeof text == 'string') {
      otext = text;
    } else if (text == null) {
      otext = "null";
      cname = "nstConsoleErrorOutput";
    } else if (typeof text != 'undefined') {
      otext = text.toString();
    } else {
      otext = "undefined";
      cname = "nstConsoleErrorOutput";
    }

    /* Open console if not already shown. */
    if (node == null) {
      // Need to cache console output if body not present yet
      var needToCache = (document.body == null);

      //
      // See if DOM is ready: mozilla browsers...
      if (document.addEventListener) {
        needToCache = needToCache || (NstConsole._DOMReady != true);

      //
      // See if document has loaded: IE browser...
      //
      // If document.readyState is available, cache output
      // if its not in a "complete" state yet.
      } else if (document.readyState) {
        needToCache = needToCache || (document.readyState != "complete");
      }

      // If we can't open the console yet, delay output by appending
      // to the cache.
      if (needToCache) {
        // Cache for when the console is opened
        NstConsole._EchoCached.push({
          _Text: text,
          _ClassName: cname
        });
        // Just ignore the attempt at this point (we need a body first)
        return;
      }

      // OK to open the console and append output now
      NstConsole.openConsole();
      node = NstConsole._OutputDiv;
    } else if (clear) {
	/* Clear current contents (if clear passed). */
	NstConsole.clear(false, true);
    }

    // See if we need to determine line length
    if (NstConsole._LineLength < 0) {
      // Pass -1 to "auto-detect"
      NstConsole.setLineLength(-1);
    }

    // Set class for output
    if (!cname) {
      cname = "nstConsoleEchoOutput";
    }

    var oarray = otext.split("\n");
    var n = oarray.length;

    for (var i = 0; i < n; i++) {
     var line = oarray[i];
     var lineLen = line.length;
     var outLen = lineLen;

     if (NstConsole._LineLength > 0) {
       outLen = NstConsole._LineLength;
     }
      
     // Increment counter and display message
     NstConsole._DebugCounter++;

     var firstLine = true;

     for (var ofs = 0; (ofs < lineLen) || firstLine; ofs += outLen) {
      var eofs = (ofs + outLen);
      var needWrapIndicator = false;
      if (eofs >= lineLen) {
	eofs = lineLen;
      } else {
	needWrapIndicator = true;
      }
      var outLine = line.substring(ofs, eofs);

      var output = document.createElement("pre");
      output.className = cname;

      // Append a line number to first portion of line output
      if (firstLine) {
	var counter = document.createElement("span");
	counter.className = "nstConsoleLineNumber";

        var clabel = "    " + NstConsole._DebugCounter.toString() + ":";
        clabel = clabel.substring(clabel.length - 5);
        counter.appendChild(document.createTextNode(clabel));
        firstLine = false;

        output.appendChild(counter);
      }

      // Append line text to output
      output.appendChild(document.createTextNode(outLine));

      // Add line wrap indicator (if necessary)
      if (needWrapIndicator) {
	var wrapIndicator = document.createElement("span");
	wrapIndicator.className = "nstConsoleWrapIndicator";
	wrapIndicator.appendChild(document.createTextNode("\\"));
	output.appendChild(wrapIndicator);
      }

      node.appendChild(output);
     }
    }

    // Scroll to bottom of output
    node.scrollTop = node.scrollHeight;
}

/* NstConsole.eval(code, echoCode)
 *
 * Evaluates "code" and displays results (or error) in debug output area.
 *
 * code - Code to evaluate.
 *
 * echoCode - Pass true if you want to echo the code which is being evaluated. */

NstConsole.eval = function(code, echoCode) {
  if (code == "") {
    return;
  }

    try {
      if (echoCode) {
	  NstConsole.echo(code, false, "nstConsoleEvalOutput");
      }

      return eval(code);

    } catch (e) {
      NstConsole.echoException(e);
      // Let error go to standard console as well
      throw(e);
    }

    NstConsole.scrollTo();
}

/* NstConsole.echoException(e)
 *
 *   Helper method to echo out a JavaScript Exception object (including
 *   a stack trace if possible).
 *
 * e - Excpetion to dump to console.
 */

NstConsole.echoException = function(e) {
  var eclass = "nstConsoleErrorOutput";
  NstConsole.var_dump(e, eclass);
  if (e.stack) {
    NstConsole.echo("Stack Trace:\n" + e.stack, false, eclass);
  }
}

/* NstConsole.evalUserInput(code)
 *
 * Evaluate code which was entered by user (and save it as the initial
 * state of the text area when creating the console).
 *
 * code - The JavaScript code to evaluate.
 */

NstConsole.evalUserInput = function(code) {

  var results = NstConsole.eval(code, true);
  if (results) {
    NstConsole.var_dump(results);
  }

}

/* NstConsole.var_dump(obj)
 *
 * Does a "debug dump" of all of the object's properties.
 * 
 * cname - Optional class name for <pre> area. */

NstConsole.var_dump = function(obj, cname) {

  if (typeof obj == "object") {
    NstConsole.echo(obj, false, cname);
    NstConsole.echo("properties {", false, cname);

    var props = new Array();
    var prop;

    for (prop in obj) {
      props.push(prop);
    }

    props.sort();

    for (var i=0; i < props.length; i++) {
      prop = props[i];

      // Show property
      var line = "  " + prop + ": ";

      // Try to get property type
      try {
        line += (typeof obj[prop]);
      } catch (verr) {
        line += "***UNABLE TO DETERMINE TYPE***";
        NstConsole.echo(line, false, "nstConsoleErrorOutput");
        continue;
      }

      // Try to append string version of property
      try {
	line += ' (' + obj[prop] + ')';
      } catch (verr) { }
      NstConsole.echo(line, false, cname);
    }

    NstConsole.echo("}", false, cname);
  } else {
    // Not a object, just echo out value
    NstConsole.echo(obj, false, cname);
  }
}

/* NstConsole.gid()
 *
 *   Handler for the "GID" button (helper for inserting a getElementById()
 *   block of text into the input window). */

NstConsole.gid = function() {

  NstConsole.insert("var e" + NstConsole._EntityCnt++ 
                    + " = document.getElementById('", "');");

}

/* NstConsole.getActiveInput()
 *
 *   Returns DOM node of active input. */

NstConsole.getActiveInput = function() {
  var iarea = document.getElementById("nstConsoleInput");
  if ((iarea != null) && (iarea.style.display != 'none')) {
    return iarea;
  }

  return document.getElementById("nstConsoleOneLiner");
}

/* NstConsole.clear(clearInput, clearOutput)
 *
 * Permits one to clear the input and/or output areas associated with the
 * console. */

NstConsole.clear = function(clearInput, clearOutput)	{

    // Clear input area (if necessary)
    if (clearInput) {
      var activeInput = NstConsole.getActiveInput();
      if (activeInput != null) {
        activeInput.value = '';
      }
    }

    // Clear output area (if necessary)
    if (clearOutput) {
	var dout = NstConsole._OutputDiv;
	while (dout.firstChild) {
	    dout.removeChild(dout.firstChild);
	}
	this._DebugCounter = 0;
    }
}

/* NstConsole.insert(before, after, [input]) - Inserts text before and/or after
 * the current insertion point in the console text area.
 *
 * before - ASCII text to insert before insertion point.
 * after - ASCII text to insert after insertion point.
 * input - Optional input/textarea to direct insertion to (if omitted,
 * defaults to main text area). */

NstConsole.insert = function(before, after, input) {
  
  if (!input) {
    input = NstConsole.getActiveInput();
    //    input = document.getElementById("nstConsoleInput");
  }

  if (input) {
    input.focus();
    if (input.setSelectionRange) {
      var selectionStart = input.selectionStart;
      var selectionEnd = input.selectionEnd;
      var selectionLen = selectionEnd - selectionStart;
      // Position of selection start AFTER inserting the before text
      var selectionStartAfter = selectionStart + before.length;
      var selectionEndAfter = selectionStartAfter + selectionLen;



      input.value = input.value.substring(0, selectionStart)
	  + before
	  + input.value.substring(selectionStart, selectionEnd)
	  + after
	  + input.value.substring(selectionEnd);

      // Reset selection area
      input.focus();
      input.setSelectionRange(selectionStartAfter, selectionEndAfter);

    } else if (document.selection) {
      input.focus();
      var range = document.selection.createRange();

      if (range.parentElement() == input) {
        var isCollapsed = (range.text == '');
        range.text = before + range.text + after;

        if (!isCollapsed) {
          range.moveEnd('character', +before.length);
          range.moveStart('character', +before.length);
	  range.select();
        }
      }
    }
    // Try forcing focus back again
    input.focus();
  }

}

/* NstConsole.checkKeyCodes(inode, event) - Looks for following keycodes:
 *
 * MOD+Enter - Evaluate what is currently typed in.
 * MOD+c - Clear the output area.
 * MOD+i - Clear the input area.
 * MOD+b - Clear both the output/input areas.
 * MOD+e - Insert a "echo" stub.
 * MOD+d - Insert a "setDebugLevel" stub.
 * MOD+g - Insert a "document.getElementById()" stub.
 * MOD+v - Insert a "var_dump" stub.
 * MOD+t - Toggle single/multi-line input areas.
 *
 * NOTE: Currently MOD is "Alt+Ctrl"
 *
 * inode - The DOM node (input/textarea entity) which triggered the event.
 * event - The event which has occurred. */

NstConsole.checkKeyCodes = function(inode, event) {
  if (!event) {
      event = window.event;
  }

  if (event) {
    // Make sure the proper modifier is pressed
    var modPressed = false;
    if (event.modifiers) {
	var modMask = (Event.ALT_MASK | Event.CONTROL_MASK);
	modPressed = ((event.modifiers & modMask) == modMask);
    } else {
	modPressed = (event.altKey && event.ctrlKey);
    }

    

    if (modPressed) {
      var keyCode = event.which ? event.which : event.keyCode;

      if (keyCode == 13) { // MOD+Enter
        var text = inode.value;
	NstConsole.evalUserInput(text);
	event.cancelBubble = true;
	event.returnValue = false;
        return false;
      } else if (keyCode == 69) { // MOD+e - stub in "NstConsole.echo();"...
        NstConsole.insert("NstConsole.echo(", ");", inode)
	event.cancelBubble = true;
	event.returnValue = false;
        return false;
      } else if (keyCode == 71) { // MOD+g - stub in a "getElementById()" helper
        NstConsole.gid();
	event.cancelBubble = true;
	event.returnValue = false;
        return false;
      } else if (keyCode == 68) { // MOD+d - stub in "NstConsole.setDebugLevel();"...
        NstConsole.insert("NstConsole.setDebugLevel(0", ");", inode)
	event.cancelBubble = true;
	event.returnValue = false;
        return false;
      } else if (keyCode == 67) { // MOD+c - clear output region...
	NstConsole.clear(false, true);
	event.cancelBubble = true;
	event.returnValue = false;
        return false;
      } else if (keyCode == 73) { // MOD+i - clear input region...
        NstConsole.clear(true, false);
        event.cancelBubble = true;
        event.returnValue = false;
        return false;
      } else if (keyCode == 66) { // MOD+b - clear both output/input regions...
        NstConsole.clear(true, true);
        event.cancelBubble = true;
        event.returnValue = false;
        return false;
      } else if (keyCode == 86) { // MOD+v - stub in: "NstConsole.var_dump();"...
        NstConsole.insert("NstConsole.var_dump(", ");", inode)
	event.cancelBubble = true;
	event.returnValue = false;
        return false;
      } else if (keyCode == 84) { // MOD+t - toggle single/multi-line input areas...
        NstConsole.setOneLineMode(!NstConsole.isOneLineMode());
        NstConsole.scrollTo();
	event.cancelBubble = true;
	event.returnValue = false;
        return false;
      } else {
	if (NstConsole.debugLevel(9)) {
	  NstConsole.echo("keyCode = " + keyCode);
	}
      }
    // If one line mode enter pressed, evaluate command
    } else if (inode.type && (inode.type == 'text')) {
      var keyCode = event.which ? event.which : event.keyCode;

      if ((keyCode == 13) || (keyCode == 10)) { // Enter
        var text = inode.value;
        NstConsole.evalUserInput(text);
        // Uncomment to "clear" after run.
        // node.value = "";
        event.cancelBubble = true;
        event.returnValue = false;
        return false;
      } else {
	if (NstConsole.debugLevel(9)) {
	  NstConsole.echo("keyCode = " + keyCode);
	}
      }
    }
  }
}


/** Get the Y offset of the viewport within the page. */

NstConsole.getViewportY = function() {
  if (document.documentElement && document.documentElement.scrollTop) {
    // For more recent DOCTYPES
    return document.documentElement.scrollTop;
  } else if (document.body.scrollTop) {
    // For quirks mode
    return document.body.scrollTop;
  }
  // give up
  return 0;
}


/** Get the X offset of the viewport within the page. */

NstConsole.getViewportX = function() {
  if (document.documentElement && document.documentElement.scrollLeft) {
    // For more recent DOCTYPES
    return document.documentElement.scrollLeft;
  } else if (document.body.scrollLeft) {
    // For quirks mode
    return document.body.scrollLeft;
  }
  // give up
  return 0;
}

