// Last modified 07/03/2002 by GM (deleted table tags for begin and end question)
// Last modified 07/09/2001 by JO (fixed example box to work better in Netscape)
// Last modified 1999/08/18 by MM (Swing/JMF dispatcher)
// Last modified 1999/07/19 by MM (fixed audio applet)
// Last modified 1999/07/09 by JJK (added paperBox())
// Last modified 1999/07/09 by JJK (added deriveLink() & solveLink())
// Last modified 1999/07/09 by JJK (added doBox())
// Last modified 1999/07/08 by MM (fixed math \Vec and \UnitVec)
// Last modified 1999/07/08 by JJK (added exampleBox)
// Last modified 1999/07/06 by LB (added textured top-nav bar for new modules)
// Last modified 1999/07/06 by JJK (added beginContent & endContent functions)
// Last modified 1999/07/06 by JJK (added highlight box)
// Last modified 1999/07/01 by JJK (added Ryan's question-drawing routines)
// Last modified 1999/06/18 by LB (added call to updateNavigation() integrating content pages into navigation)
// Last Modified 1999/06/10 by MM (added TeX \Vec macro)
// Last Modified 1999/05/17 by BF (added size values that can be retrieved from the function call)
// Last Modified 1999/05/13 by MM (added TeX/HTML link selector)
// Last Modified 1999/04/01 by MM (added slide show)
// Last Modified 1999/03/17 by MM (changed module root finder)
// Last Modified 1999/03/08 by MM (added GIF animation/still switcher)
// Last Modified 10/20/98 by MM (changed navigation bar script).
// Last Modified 10/19/98 by MM (added biography macro).
// Last Modified 10/01/98 by MM (added TeX/HTML selector).
// Last Modified 9/30/98 by MM (added navigation bar script).
// Last Modified 9/30/98 by MM (added popupVideo).
// Last Modified 9/29/98 by MM (added embedVideo and linkVideo).
// Last Modified 9/25/98 by PK (added back in the nav scripts for derive/solve rollover).
// Last Modified 9/24/98 by VE (fixed tech requirements pop-up problem).
// Last Modified 9/24/98 by PK (removed navigation scripts as all mod. nav. mechanisms are updated).
// Last Modified 9/16/98 by PK (removed navigation for tracks, moved to trackscripts.js).
// Last Modified 9/15/98 by PK (added navigation for tracks).
// Last Modified 9/14/98 by MM (added Sun style sheet).
// Last Modified 9/14/98 by VE (fixed IRIX problem).
// Last Modified 9/3/98 by MM (added Tiny Quiz).
// Last Modified 8/26/98, 8/18/98, 8/11/98 by MM.
// Last Modified 7/30/98 by MM. 7/30/98 by VE. 7/15/98 by PK.
// Scripts copyright 1998 Rensselaer Polytechnic Institute.
// Authors: Luke Bellandi, Virginia Eubanks, Pamela Kott,
//          Jan Joseph Kratky, Mike Malak
// <SCRIPT>

// Contents

//

// 1) Style Sheet Finder

// 2) Navigation Bar and Solve and Derive Boxes

// 3a) Pop Up Windows (argh) (fixed size)

// 3b) Pop Up Windows (size values can be passed in)

// 4) Math Notation

// 5) Video Dispatcher

// 6) Audio Dispatcher

// 7) Tiny Quiz

// 8) TeX/HTML selector

// 9) Mathematician biography

// 10) GIF89a Animation/Still switcher

// 11) Slide Show

// 12) Call to update navigation on content page load-in

// 13) Wrapper for module questions (think boxes).

// 14) Highlight boxes.

// 15) beginContent() and endContent().

// 16) Example Boxes.

// 17) Do Boxes.

// 18) deriveLink() and solveLink()

// 19) Paper Boxes.

// 20) Swing/JMF dispatcher

// -----------------------

// (1) Style Sheet Finder 


// Style Sheet Finder tests for operating system, browser, and 
// browser version, and then document.writes a line into the
// header of each html page that points to the appropriate style
// sheet for each set up.  Works for Netscape and IE versions 3 and
// 4 on Macintosh, Win environment, Sun, AIX, IRIX, and HP.


// set variables

var whatStyle;
whatStyle = "Default";

// The first thing we need to do is find the directory with all the
// shared stuff like style sheets and graphics.  This is
// /devmodules/shared/; we have to figure out where /devmodules/ is in
// relation to the file being viewed.  It would be nice if we could
// always just say "/devmodules/", indicating that it is a
// subdirectory of the web root directory, but if we are using the
// file: protocol, as with a CD, there is no web root directory.  But
// in this case, with any luck, the tree structure will be more or
// less intact, and the file will be under one of the top-level
// directories.  We'll have to search to see which one.

// If we are using http:, then we will assume that /devmodules/ is in
// fact a top-level directory.  If you install the modules
// somewhere else in the hierarchy, you must change the following
// value.  It should begin and end with a slash.
var http_module_root = "/devmodules/";

// If we are running off a CD, or for some other reason not using
// http:, then we have to search through the current page's location
// for the name of (what is supposed to be) a top-level directory.
// These are sort of in order by how likely they are to be hit; also,
// words which might be subdirectories (like "shared") come last.
var top_level_directories = new Array();
top_level_directories[top_level_directories.length++] = "devmodules";
top_level_directories[top_level_directories.length++] = "webhtml";
top_level_directories[top_level_directories.length++] = "otm";
top_level_directories[top_level_directories.length++] = "oldmodules";
top_level_directories[top_level_directories.length++] = "search";
top_level_directories[top_level_directories.length++] = "documentation_mw";
top_level_directories[top_level_directories.length++] = "codebase";
top_level_directories[top_level_directories.length++] = "restricted";
top_level_directories[top_level_directories.length++] = "webgraphics";
top_level_directories[top_level_directories.length++] = "vrml";
top_level_directories[top_level_directories.length++] = "ibm";
top_level_directories[top_level_directories.length++] = "shared";

// The following does not begin with a slash.
var file_module_root = "devmodules/";

var module_root_search_string;
var module_root_string = null;
function module_root()
  {
    var h, i = -1, j, z;

    // Maybe we've already done this.  Maybe not.
    if (module_root_string == null)
      {
	h = new String(document.location);

	if (h.indexOf("http:") == 0)
	  {
	    module_root_string = http_module_root;
	  }
	else
	  {
	    // OK, we are probably using file:.
	    for (z = 0; (z < top_level_directories.length) && (i < 0); z++)
	      {
		module_root_search_string = top_level_directories[z];
		i = h.indexOf(module_root_search_string);
	      }
	    if (i < 0)
	      {
		// Nothing?
		// Maybe it's the Links home page.
		// Better hope so.
		module_root_string =
		  h.substring(0, h.lastIndexOf("/") + 1) + file_module_root;
	      }
	    else
	      {
		module_root_string = h.substring(0, i) + file_module_root;
	      }
	  }
      }
    //alert(module_root_string);
    return (module_root_string);
  }

//alert(module_root());


function getStyle() {
	// OS
	if (navigator.userAgent.indexOf("Win") >= 0) {
		
		// find which browser
		if (navigator.appName.indexOf("Netscape") >= 0) {
				
			// find which version
			if (navigator.appVersion.indexOf("3.") >=0) {
					whatStyle= "Win_Net_4";
			}
			else if (navigator.appVersion.indexOf("4.") >=0) {
					whatStyle= "Win_Net_4";
			}
			else if (navigator.appVersion.indexOf("5.") >=0) {
					whatStyle= "Win_Net_4";
			}
		}
					
		else if (navigator.appName.indexOf("Microsoft") >= 0) {
					
			// find which version
			if (navigator.appVersion.indexOf("3.") >=0) {
					whatStyle= "Win_Ie_4";
			}
			else if (navigator.appVersion.indexOf("4.") >=0) {
					whatStyle= "Win_Ie_4";
			}
			else if (navigator.appVersion.indexOf("5.") >=0) {
					whatStyle= "Win_Ie_4";
			}
		}
	}		
	else if (navigator.appVersion.indexOf("Mac") >= 0) {
		
		// find which browser
		if (navigator.appName.indexOf("Netscape") >= 0) {
				
			// find which version
			if (navigator.appVersion.indexOf("3.") >=0) {
				whatStyle= "Mac_Net_4";
			}
			else if (navigator.appVersion.indexOf("4.") >=0) {
				whatStyle= "Mac_Net_4";
			}
			else if (navigator.appVersion.indexOf("5.") >=0) {
				whatStyle= "Mac_Net_4";
			}
		}
		else if (navigator.appName.indexOf("Microsoft") >= 0) {
					
			// find which version
			if (navigator.appVersion.indexOf("3.") >=0) {
				whatStyle= "Mac_Ie_4";
			}
			if (navigator.appVersion.indexOf("4.") >=0) {
				whatStyle= "Mac_Ie_4";
			}
			if (navigator.appVersion.indexOf("5.") >=0) {
				whatStyle= "Mac_Ie_4";
			}
		}
	}
								
	else if ((navigator.appVersion.indexOf("AIX")>=0)||
		   (navigator.appVersion.indexOf("IRIX")>=0)||
		   (navigator.appVersion.indexOf("irix")>=0)||
		   (navigator.appVersion.indexOf("hpux")>=0)||
		   (navigator.appVersion.indexOf("linux")>=0)||
		   (navigator.appVersion.indexOf("bsd")>=0)||
		   (navigator.appVersion.indexOf("freebsd")>=0)||
		   (navigator.appVersion.indexOf("sco")>=0)||
		   (navigator.appVersion.indexOf("unixware")>=0)||
		   (navigator.appVersion.indexOf("mpras")>=0)||
		   (navigator.appVersion.indexOf("reliant")>=0)||
		   (navigator.appVersion.indexOf("dec")>=0)||
		   (navigator.appVersion.indexOf("sinix")>=0)||
		   (navigator.appVersion.indexOf("Sun")>= 0)) 

		{
		
		// find which browser
		if (navigator.appName.indexOf("Netscape") >= 0) {
			// find which version
			if (navigator.appVersion.indexOf("3.") >=0) {
				whatStyle= "Unix_Net_4";
			}
			else if (navigator.appVersion.indexOf("4.") >=0)			  {
			   whatStyle= "Unix_Net_4";
			   if (navigator.appVersion.indexOf("Sun")>= 0)			     {			       whatStyle = "Unix_Sun_Net_4";			     }			}
			else if (navigator.appVersion.indexOf("5.") >=0) {
				whatStyle= "Unix_Net_4";
			}
		}
		else if (navigator.appName.indexOf("Microsoft") >= 0) {
			// find which version
			if (navigator.appVersion.indexOf("3.") >=0) {
				whatStyle= "Unix_Ie_4";
			}
			else if (navigator.appVersion.indexOf("4.") >=0) {
				whatStyle= "Unix_Ie_4";
			}
			else if (navigator.appVersion.indexOf("5.") >=0) {
				whatStyle= "Unix_Ie_4";
			}
		}
	}

}
getStyle();	

document.write('<LINK REL=StyleSheet HREF="' + module_root() +
     	       'shared/style/' + whatStyle +
               '.css" TYPE="text/css">');

//document.write('<LINK REL=StyleSheet HREF="'+web_root+'/devmodules/shared/style/'+whatStyle+'.css" TYPE="text/css">');

//document.write('<LINK REL=StyleSheet HREF="../style/'+whatStyle+'.css" TYPE="text/css" MEDIA="screen">');



// -----------------------

// (2) Navigation 





var deriv = new Image();
deriv.src = "/devmodules/shared/graphics/deriv.gif";
var deriv3 = new Image();
deriv3.src = "/devmodules/shared/graphics/deriv3.gif";

var solve = new Image();
solve.src = "/devmodules/shared/graphics/deriv.gif";
var solve3 = new Image();
solve3.src = "/devmodules/shared/graphics/deriv3.gif";


// Navigation Bar Maker Script
// Heading Colors:
var unused_color_string = "#cccccc";
var thispage_color_string = "#6b1018";
var link_color_string = "#4a4a84";
//
function ending_filename(s)
{
  var i;
  if (s == null) return null;
  i = s.lastIndexOf("/");
  if (i >= 0)
    {
      return s.substring(i + 1, s.length);
    }
  else
    {
      return s;
    }
}

var thispage = ending_filename(document.location.href);
function makeNavigationBar(th, exp, ap, di, exc)
{

  // the code in the 'if' block is deprecated: old module scheme.  It is to eventually be replaced with the code
  // in the 'else' block.
  if(typeof(top.moduleTitle)=="undefined")
  {
    document.writeln('<TABLE CELLSPACING="0" CELLPADDING=0 BORDER=0 WIDTH=100% BGCOLOR=linen>');
    document.writeln('<TR><TD>&nbsp;</TD><TD>&nbsp;</TD>');
    document.writeln('<TD>&nbsp;</TD><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>');
    document.writeln('<TR>');
    make_cell("concepts", th);
    make_cell("discover", exp);
    make_cell("applications", ap);
    make_cell("collaborate", di);
    make_cell("practice", exc);
    document.writeln("</TR></TABLE>");
  }
  else
  {
    document.writeln('<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100% BGCOLOR=E9E3D5 ' +
                     'BACKGROUND="' + top.baseDir + 'shared/navigation/graphics/bkgtile.jpg">');

    document.writeln('<TR><TD HEIGHT=50><IMG SRC="' + top.baseDir + 'shared/navigation/graphics/bkgVertEdgeL.jpg"></TD>');

    make_cell("concepts", th);
    make_cell("discover", exp);
    make_cell("applications", ap);
    make_cell("collaborate", di);
    make_cell("practice", exc);

    document.writeln('<TD HEIGHT=50><IMG SRC="' + top.baseDir + 'shared/navigation/graphics/bkgVertEdgeR.jpg"></TD></TR>');
 
    document.writeln('<TR><TD HEIGHT=11 WIDTH=11><IMG SRC="' + top.baseDir + 'shared/navigation/graphics/bkgCornerL.jpg"></TD>');
    document.writeln('<TD HEIGHT=11 COLSPAN=5>');
    document.writeln('<TABLE WIDTH=100% BACKGROUND="' + top.baseDir + 'shared/navigation/graphics/bkgHorizEdge.jpg" CELLPADDING=0 CELLSPACING=0 BORDER=0>');
    document.writeln('<TR><TD><IMG SRC="' + top.baseDir + 'shared/navigation/graphics/transparent.gif" HEIGHT=11></TD></TR>');
    document.writeln('</TABLE></TD>');
    document.writeln('<TD HEIGHT=11 WIDTH=11><IMG SRC="' + top.baseDir + 'shared/navigation/graphics/bkgCornerR.jpg"></TD>');
    document.writeln('</TR></TABLE>');
  }
}

function make_cell(heading, fullref)
  {
    var ref = null;
    document.writeln('<TD WIDTH="20%">');
    document.writeln('<CENTER>');
    if (fullref != null)
      {
	ref = ending_filename(fullref);
      }
    if (ref==null)
      document.write('<DIV CLASS="navbarunused">');
    else if (ref == thispage)
    {
      /* BACKWARD COMPATIBILITY: catch for the old navigation's ">" */
      if(typeof(top.bulletOn)!="undefined")
        document.write('<DIV CLASS="navbarthispage"><IMG SRC="' + top.bulletOn.src + '">&nbsp;');
      else document.write('<DIV CLASS="navbarthispage">&gt;&nbsp;');
    }
    else
      document.write('<A CLASS="navbarlink" HREF="' + fullref + '">');
    document.write(heading);
    if (ref != null && ref != thispage)
      {
	document.writeln('</A>');
      }
    else
      {
	document.writeln('</DIV>');
      }
    document.writeln('</CENTER>');
    document.writeln('</TD>');
  }


function gotoRelatedModule()
{
  var si, newlink;
  si = document.relatedmodules.optionselect.selectedIndex;

  newlink = document.relatedmodules.optionselect.options[si].value;

  if ((newlink != null) && (newlink != '') && (newlink != 'undefined') &&
      (newlink != "Choose a Related Module"))
    {
      parent.window.location = (newlink);
    }
  else
    {
      alert('Please choose a related module from the list.');
    }
}

// End Navigation 

// -----------------






// (3a) Pop Up Windows 



function popUpAnswer(whichHTMLfile) {

	remoteWin = window.open( whichHTMLfile, "popUpAnswer", "width=320 ,height=240 ,resizable=yes,status=no,menubar=yes,scrollbars=yes" );

    remoteWin.focus()
    
}


// End Pop Up Windows

// -----------------



// (3b) Pop Up Windows 



function popUpAnswersize(whichHTMLfile, w, h) {
	
	var params = 'width='+w+' ,height='+h+' ,resizable=yes,status=no,menubar=yes,scrollbars=yes';
	
	remoteWin = window.open( whichHTMLfile, "popUpAnswer", params );
    remoteWin.focus()

}


// End Pop Up Windows





// -----------------------

// (4) Math Notation 


// If the boolean arg is_display is true, then the plugin or applet
// will be shown within some kind of display environment, maybe a
// table.  The embed... functions don't have to create the space.
// They may need to alter the formatting.
var webeq_font_size = 17;
function setWebEQFontSize(n)
  {
    webeq_font_size = n;
  }

function hex_digit_to_number(h)
 {
   if (h == "0") return 0;
   else if (h == "1") return 1;
   else if (h == "2") return 2;
   else if (h == "3") return 3;
   else if (h == "4") return 4;
   else if (h == "5") return 5;
   else if (h == "6") return 6;
   else if (h == "7") return 7;
   else if (h == "8") return 8;
   else if (h == "9") return 9;
   else if ((h == "a") || (h == "A")) return 10;
   else if ((h == "b") || (h == "B")) return 11;
   else if ((h == "c") || (h == "C")) return 12;
   else if ((h == "d") || (h == "D")) return 13;
   else if ((h == "e") || (h == "E")) return 14;
   else if ((h == "f") || (h == "F")) return 15;
  }
function two_hex_digits_to_number(hh)
  {
    return (16 * hex_digit_to_number(hh.substring(0, 1)) +
		 hex_digit_to_number(hh.substring(1, 2)));
  }
function hex_rrggbb_to_tcx_rgb(nrrggbb)
  {
    var r, g, b;
    var rr, gg, bb;
    rr = nrrggbb.substring(1, 3);
    gg = nrrggbb.substring(3, 5);
    bb = nrrggbb.substring(5, 7);
    r = two_hex_digits_to_number(rr);
    g = two_hex_digits_to_number(gg);
    b = two_hex_digits_to_number(bb);
    return ("\\rgb{" + r + "}{" + g + "}{" + b + "}");
  }
// pwidth and pheight are in pixels.
function embedTeXMathWithTechexplorer(tex_string, pwidth, pheight,
				      is_display, width, height)
  {
    document.writeln('<embed type="application/x-techexplorer" ');
    document.write('texdata="\\pagecolor{');
    document.write(hex_rrggbb_to_tcx_rgb(document.bgColor) + "}");
    document.write("\\newcommand{\\Vec}[1]{\\vec{\\mathbf{#1}}}");
    document.write("\\newcommand{\\UnitVec}[1]{\\hat{\\mathbf{#1}}}");
    document.write("\\newcommand{\\href}[2]{\\docLink{#1}{#2}}");
    document.write("\\newcommand{\\statusline}[1]{}");
    document.write("\\newcommand{\\fghighlight}[2]{#2}");
    document.write("\\newcommand{\\bghighlight}[2]{#2}");
    document.write("\\newcommand{\\toggle}[4]{\\altLink{#1}{#2}}");
    document.write("\\color{" + hex_rrggbb_to_tcx_rgb(document.fgColor) + "}");
    if (is_display) document.writeln("$$"); else document.write("$");
    document.write(tex_string);
    if (is_display) document.write("$$"); else document.write("$");
    document.writeln('" ');
    document.writeln('height="' + pheight + '" width="' + pwidth + '" ');
    if (is_display || (height > 1.2))
      document.writeln('align="absmiddle"');
    else
      document.writeln('align="absbottom"');
    document.writeln('pluginspage=' +
		     '"http://www.software.ibm.com/techexplorer/">');
  }

function embedTeXMathWithWebEQ(tex_string, pwidth, pheight,
			       is_display, width, height)
  {
//     var Vec_string = "\\Vec{";
//     var vec_string = "\\vec{\\mathbf{";
//     var UnitVec_string = "\\UnitVec{";
//     var unitvec_string = "\\hat{\\mathbf{";
    document.writeln("<applet codebase=/webeq/classes/ ");
    document.writeln("archive=\"webeq.zip\" ");
    document.writeln("code=\"geom.webeq.app.mdraw\" ");
    document.writeln("height=\"" + pheight + "\" width=\"" + pwidth + "\" ");
    if (is_display || (height > 1.2))
      document.writeln('align="absmiddle">');
    else
      document.writeln('align="absbottom">');
    document.writeln("<param name=cabbase value=\"webeq.cab\"> ");
    document.writeln("<param name=color value=\"" + document.bgColor + "\"> ");
    document.writeln("<param name=size value=" +  webeq_font_size + "> ");
    document.writeln("<param name=\"macros\" value=\"" +
		     "\\define{\\UnitVec}[1]{\\hat{\\mathbf{#1}}}" +
		     "\\define{\\Vec}[1]{\\vec{\\mathbf{#1}}}" +
		     "\\define{\\varphi}{\\phi}" +
		     "\\define{\\varpi}{\\pi}" +
		     "\\define{\\vartheta}{\\theta}" +
		     "\\define{\\varrho}{\\rho}" +
		     "\\define{\\varepsilon}{\\epsilon}" +
		     "\\define{\\qquad}{\\quad\\quad}" +
		     "\\define{\\docLink}[2]{\\href{#1}{#2}}" +
		     "\\define{\\altLink}[2]{\\toggle{#1}{#2}{Click}{Click}}" +
		     "\">");
    document.write("<param name=eq value=\"")
    //document.write("\\define{\\Vec}{\\vec}");
    document.write("\\fontcolor{" + document.fgColor + "}{");
    if (is_display) document.write("\\displaystyle{");

//     var pos = 0;
//     var bpos;
//     var len = Vec_string.length;
//     pos = tex_string.indexOf(Vec_string);
//     while (pos != -1)
//       {
// 	pr = tex_string.substring(0, pos);
// 	po = tex_string.substring(pos + len, tex_string.length);
// 	bpos = po.indexOf("}");
// 	br = po.substring(0, bpos);
// 	bo = po.substring(bpos, po.length);
// 	//alert ("br = " + br);
// 	//alert ("bo = " + bo);
// 	tex_string = pr + vec_string + br + "}" + bo;
// 	pos = tex_string.indexOf(Vec_string);
//       }
// 
//     len = UnitVec_string.length;
//     pos = tex_string.indexOf(UnitVec_string);
//     while (pos != -1)
//       {
// 	pr = tex_string.substring(0, pos);
// 	po = tex_string.substring(pos + len, tex_string.length);
// 	bpos = po.indexOf("}");
// 	br = po.substring(0, bpos);
// 	bo = po.substring(bpos, po.length);
// 	//alert ("br = " + br);
// 	//alert ("bo = " + bo);
// 	tex_string = pr + unitvec_string + br + "}" + bo;
// 	pos = tex_string.indexOf(UnitVec_string);
//       }

    document.write(tex_string);
    if (is_display) document.write("}");
    document.writeln("}\">");
  }
function endApplet()
  {
    document.write("</applet>");
  }
// The following emergency backup just prints the TeX string.
function embedTeXMathWithChutzpah(tex_string, pwidth, pheight,
				  is_display, width, height)
  {
    if (is_display)
      {
	document.write("<table border=2 ");
	document.write("height= " + pheight + " width=" + pwidth + " ");
	document.write("align=absmiddle><tr><td align=center>");
      }
    document.write("<font color=red>" + tex_string + "</font>");
    if (is_display)
      {
	document.write("</td></tr></table>");
      }
  }
function embedTeXMath(tex_string, pwidth, pheight,
		      is_display, width, height)
  {
    if (has_techexplorer)
      embedTeXMathWithTechexplorer(tex_string, pwidth, pheight,
				   is_display, width, height);
    else
      {
        embedTeXMathWithWebEQ(tex_string, pwidth, pheight,
			      is_display, width, height);
        embedTeXMathWithChutzpah(tex_string, pwidth, pheight,
				 is_display, width, height);
        endApplet();
      }
  }
// The following are guesses as to the width of a notional standard
// character (N) and the height of a line of math text.
var char_width;
var char_height;
var display_pad_chars;
var line_pad_chars;

function display(tex_string, guess_chars, guess_lines)
  {
    if (guess_chars == null) guess_chars = 1; // becomes guess_lines....
    if (guess_lines == null) { guess_lines = guess_chars; guess_chars = 40; }
    var pwidth = (guess_chars + display_pad_chars) * char_width;
    var pheight = (guess_lines + line_pad_chars) * char_height;
    document.write("<br><center><br>");
    document.write("<table border=0>");
    document.writeln("<tr><td>");
    embedTeXMath(tex_string, pwidth, pheight,
		 true, guess_chars, guess_lines);
    document.write("</td></tr></table></center><br>");
  }
//function xxdisplay(tex_string, guess_lines)
//  {
//    Display(tex_string, 40, guess_lines);
//  }
function math(tex_string, guess_chars, guess_lines)
  {
    if (guess_lines == null) guess_lines = 1;
    var pwidth = (guess_chars + display_pad_chars) * char_width;
    var pheight = (guess_lines + line_pad_chars) * char_height;
    embedTeXMath(tex_string, pwidth, pheight,
		 false, guess_chars, guess_lines);
  }
//function xxmath(tex_string, guess_chars)
//  {
//    Math(tex_string, guess_chars, 2);
//  }
var has_techexplorer = false;
function check_for_techexplorer()
  {
    var p;
    p = navigator.mimeTypes["application/x-techexplorer"];
 
    if (p == null || p == "undefined")
      {
        has_techexplorer = false;
      }
    else
      {
        p = p.enabledPlugin;
        if (p != null && (p.name.indexOf("IBM techexplorer") != -1 ||
                          p.description.indexOf("IBM techexplorer") != -1))
          has_techexplorer = true;
        else
          has_techexplorer = false;
        //alert("TESTING: " + p + ", " + has_techexplorer);
      }
  }
if (whatStyle.indexOf("Mac_Ie") < 0) check_for_techexplorer();


if (has_techexplorer)
  {
    char_width = 11;
    char_height = 14;
    display_pad_chars = 2;
    line_pad_chars = 1;
  }
else // WebEQ
  {
    if (navigator.appVersion.indexOf("Win") >= 0 ||
	navigator.appVersion.indexOf("Mac") >= 0)
	  char_width = 12;
    else
	  char_width = 11;
    char_height = 14;
    display_pad_chars = 0.5;
    line_pad_chars = .5;
  }

// That was complicated.  For when you just want one symbol, and you
// want it to be usable as a hyperlink anchor, try these:

var image_base;
image_base = module_root() + "shared/graphics/symbols/";
var use_symbol_font = (whatStyle.indexOf("Mac") >= 0 ||
		       whatStyle.indexOf("Win") >= 0);
function symbol(c, i, w, h, a)
  {
    if (use_symbol_font && (c != null))
      {
	document.write('<font face="symbol">' + c + '</font>');
      }
    else
      {
	document.write('<img src="' + image_base + i + '" ');
	document.write('width="' + w + '" height="' + h + '" ');
	if (a != null) document.write('align="' + a + '" ');
	document.write('border="0"></img>');
      }
  }

function alpha() { symbol("a", "alpha.gif", 10, 9); }
function beta() { symbol("b", "beta.gif", 10, 18, "absbottom"); }
function gamma() { symbol("g", "gamma.gif", 9, 13, "absbottom"); }
function delta() { symbol("d", "delta.gif", 7, 14); }
function epsilon() { symbol(null, "epsilon.gif", 6, 9); }
function varepsilon() { symbol("e", "varepsilon.gif", 7, 9); }
function zeta() { symbol("z", "zeta.gif", 7, 18, "absbottom"); }
function eta() { symbol("h", "eta.gif", 8, 13, "absbottom"); }
function theta() { symbol("q", "theta.gif", 7, 14); }
function vartheta() { symbol("J", "vartheta.gif", 8, 14); }
function iota() { symbol("i", "iota.gif", 5, 9); }
function kappa() { symbol("k", "kappa.gif", 9, 9); }
function lambda() { symbol("l", "lambda.gif", 8, 14); }
function mu() { symbol("m", "mu.gif", 10, 13, "absbottom"); }
function nu() { symbol("n", "nu.gif", 8, 9); }
function xi() { symbol("x", "xi.gif", 6, 18, "absbottom"); }
function pi() { symbol("p", "pi.gif", 9, 9); }
function varpi() { symbol("v", "varpi.gif", 14, 9); }
function rho() { symbol("r", "rho.gif", 10, 14, "absbottom"); }
function varrho() { symbol(null, "varrho.gif", 9, 13, "absbottom"); }
function sigma() { symbol("s", "sigma.gif", 9, 9); }
function varsigma() { symbol("V", "varsigma.gif", 7, 11, "absbottom"); }
function tau() { symbol("t", "tau.gif", 8, 9); }
function upsilon() { symbol("u", "upsilon.gif", 8, 9); }
function phi() { symbol("f", "phi.gif", 9, 18, "absbottom"); }
function varphi() { symbol("j", "varphi.gif", 9, 13, "absbottom");}
function chi() { symbol("c", "chi.gif", 10, 13); }
function psi() { symbol("y", "psi.gif", 10, 18, "absbottom"); }
function omega() { symbol("w", "omega.gif", 10, 9); }
function Gamma() { symbol("G", "UGamma.gif", 10, 14); }
function Delta() { symbol("D", "UDelta.gif", 13, 14); }
function Theta() { symbol("Q", "UTheta.gif", 12, 14); }
function Lambda() { symbol("L", "ULambda.gif", 11, 14); }
function Xi() { symbol("X", "UXi.gif", 10, 14); }
function Pi() { symbol("P", "UPi.gif", 14, 14); }
function Sigma() { symbol("S", "USigma.gif", 11, 14); }
function Upsilon() { symbol("U", "UUpsilon.gif", 12, 14); }
function Phi() { symbol("F", "UPhi.gif", 12, 14); }
function Psi() { symbol("Y", "UPsi.gif", 12, 14); }
function Omega() { symbol("W", "UOmega.gif", 11, 14); }
function aleph() { symbol("&#192;", "aleph.gif", 10, 14); }
function del() { symbol("&#182;", "del.gif", 9, 14); }
function Del() { symbol("&#209;", "UDel.gif", 14, 14); }
function emf() { symbol(null, "emf.gif", 10, 15); }
function hbar() { symbol(null, "hbar.gif", 10, 14); }
function imaginary() { symbol("&#193;", "imaginary.gif", 13, 14); }
function imath() { symbol(null, "imath.gif", 6, 9); }
function jmath() { symbol(null, "jmath.gif", 8, 13, "absbottom"); }
function ell() { symbol(null, "ell.gif", 7, 14); }
function emptyset() { symbol("&#198;", "emptyset.gif", 8, 18); }
function wp() { symbol("&#195;", "weierstrass.gif", 11, 14, "absbottom"); }
function weierstrass() { wp(); }
function coprod() { symbol(null, "coprod.gif", 12, 14); }
function real() { symbol("&#194;", "real.gif", 13, 15); }
function infinity() { symbol("&#163;", "infinity.gif", 18, 9); }
function forall() { symbol("&#034;", "forall.gif", 11, 14); }
function exists() { symbol("&#036;", "exists.gif", 9, 14); }
function ominus() { symbol(null, "ominus.gif", 14, 14, "absmiddle"); }
function oplus() { symbol("&#197;", "oplus.gif", 13, 14, "absmiddle"); }
function oslash() { symbol(null, "oslash.gif", 14, 14, "absmiddle"); }
function odot() { symbol(null, "odot.gif", 9, 9, "absmiddle"); }
function otimes() { symbol("&#196;", "otimes.gif", 9, 9, "absmiddle"); }

function ne() { symbol(null, "ne.gif", 13, 20, "absmiddle"); }
function le() { symbol(null, "le.gif", 12, 16, "absmiddle"); }
function ge() { symbol(null, "ge.gif", 12, 16, "absmiddle"); }
function neq() { symbol(null, "ne.gif", 13, 20, "absmiddle"); }
function leq() { symbol(null, "le.gif", 12, 16, "absmiddle"); }
function geq() { symbol(null, "ge.gif", 12, 16, "absmiddle"); }
function pm() { symbol(null, "pm.gif", 13, 14, "absmiddle"); }

function spade() { symbol("&#170;", "spade.gif", 13, 18); }
function heart() { symbol("&#169;", "heart.gif", 13, 15); }
function club() { symbol("&#167;", "club.gif", 14, 18); }
function diamond() { symbol("&#168;", "diamond.gif", 13, 17); }
function flat() { symbol(null, "flat.gif", 5, 15); }
function natural() { symbol(null, "natural.gif", 5, 20); }
function sharp() { symbol(null, "sharp.gif", 6, 20); }
function integral() { symbol(null, "int.gif", 12, 31, "absmiddle"); }
function iint() { symbol(null, "iint.gif", 19, 31, "absmiddle"); }
function iiint() { symbol(null, "iiint.gif", 26, 31, "absmiddle"); }
function oint() { symbol(null, "oint.gif", 18, 46, "absmiddle"); }
function oiint() { symbol(null, "oiint.gif", 27, 46, "absmiddle"); }

function rsymbol(c, i, w, h, a)
  {
    if (c != null)
      {
	document.write(c);
      }
    else
      {
	document.write('<img src="' + image_base + i + '" ');
	document.write('width="' + w + '" height="' + h + '" ');
	if (a != null) document.write('align="' + a + '" ');
	document.write('border="0"></img>');
      }
  }

function dagger() { rsymbol(null, "dagger.gif", 7, 16); }
function ddagger() { rsymbol(null, "ddagger.gif", 7, 18); }
function paragraph() { rsymbol("&#182;", "paragraph.gif", 11, 18); }
function section() { rsymbol("&#167;", "section.gif", 6, 18); }
function times() { rsymbol("&#215", null, 0, 0); }

// End Math Notation 
// -----------------------

// (5) Video Dispatcher

/** This returns the object (usually an Applet) that acts as the
    document's video dispatcher.  If the video dispatcher is null,
    then the Javascript must handle dispatching.  (It's not too
    complicated, but an Applet has some slightly better ways.) **/
function getVideoDispatcher()
{
  // OK, so I haven't written a VideoDispatcher yet.
  return null;
}

var VIDEO_UNKNOWN = -1;

// We will support a few video types:
//   QuickTime(.qt, .mov)
//   AVI (.avi)
//   MPEG (.mpeg, .mpe, .mpg)
//   Flic (.fli, .flc, .flic)
//   Real Video (.ra, .ram, .rm)
//   Real Video Plugin (.rpm)
//   Vivo (.viv, .vivo)
//   SGI (.mv, .movi, .movie)
//   GIF89a (.gif)
// These are in no particular order.
var VIDEO_TYPE_COUNT = 9;

var VIDEO_QUICKTIME = 0;
var VIDEO_AVI = 1;
var VIDEO_MPEG = 2;
var VIDEO_FLIC = 3;
var VIDEO_REALVIDEO = 4;
var VIDEO_RVPLUGIN = 5;
var VIDEO_VIVO = 6;
var VIDEO_SGI = 7;
var VIDEO_GIF89A = 8;

// There are four ways to embed video: using an embedded plugin, using an
// applet, using a full-screen plugin (yecch), and just using a
// hyperlink.
// Using an applet is currently not appreciated, because JMF is not
// ubiquitous, and the Java-only applets are slow.

var VIDEO_EMBED_COUNT = 4;

var VIDEO_PLUGIN = 3;
var VIDEO_APPLET = 2;
var VIDEO_FULLSCREEN = 1;
var VIDEO_LINK = 0;

//Here is an object that encapsulates what we know about
//the browser and its video plugins.
function VideoPlayerInfo(_type, _mimetype, _desc)
{
  var i;
  var _ext;
  this.type = _type;
  this.mimetype = _mimetype;
  this.description = _desc;
  this.nsuffixes = arguments.length - 2;
  this.suffix = new Array(this.nsuffixes);
  for (i = 0; i < this.nsuffixes; i++)
    {
      _ext = arguments[i+2];
      if (_ext.indexOf(".") == 0)
	this.suffix[i] = _ext;
      else
	this.suffix[1] = "." + _ext;
    }

  this.is_preferred = false;
  this.is_preferred_plugin = false;
  var preferred_type; // preferred for hyperlinked video
  var preferred_plugin_type; // preferred for plugin
  if (whatStyle.indexOf("Win") >= 0)
    {
      preferred_plugin_type = VIDEO_QUICKTIME; // I like this plugin better.
      preferred_type = VIDEO_AVI;
    }
  else if (whatStyle.indexOf("Mac") >= 0)
    {
      preferred_plugin_type = preferred_type = VIDEO_QUICKTIME;
    }
  else if (navigator.appVersion.indexOf("IRIX") >= 0)
    {
      preferred_plugin_type = preferred_type = VIDEO_SGI;
    }
  else
    {
      preferred_plugin_type = VIDEO_UNKNOWN; // Unix doesn't prefer anything
      preferred_type = VIDEO_MPEG;
    }

  if (preferred_type == this.type)
    this.is_preferred = true;

  if (preferred_plugin_type == this.type)
    this.is_preferred_plugin = true;

  // Everything starts out wanting to be a hyperlink.
  this.embed_type = VIDEO_LINK;

  // The embed function takes four args:
  // the name of the video, the width, the height, and the type-index
  // (VIDEO_QUICKTIME or whatever).
  this.embed_function = embed_video_as_hyperlink;

  this.plugin = null;
  this.navigator_can_handle = false;
  if (whatStyle.indexOf("Net") >= 0) // Only Netscape has plugin info.
    {
      var this_mimetype;
      //alert(this.mimetype + ", " + this.is_preferred_plugin);
      this_mimetype = navigator.mimeTypes[this.mimetype];
      if (this_mimetype != null && this_mimetype != "undefined")
	{
	  this.navigator_can_handle = true;
	  this.plugin = this_mimetype.enabledPlugin;
	  if (this.plugin == "undefined") this.plugin = null;
	}
      else
	{
	  this.plugin = null;
	}
    }

  this.plugin_type = null; // Short name of the plugin.
  this.control_panel_height = 0; // We have to add this to the HEIGHT.

  // Now, what do we know about the plugin?
  if (this.type == VIDEO_GIF89A)
    {
      this.embed_function = embed_video_as_gif89a;
      this.embed_type = VIDEO_PLUGIN; // It's not really a plugin!
    }
  else if ((whatStyle.indexOf("Ie") >= 0) &&
	   (this.type == VIDEO_AVI)) // IE can handle AVI.
    {
      this.embed_function = embed_video_as_dynsrc;
      this.embed_type = VIDEO_PLUGIN; // sort of
      this.control_panel_height = 0;
      this.plugin_type = "IE built-in AVI player";
    }
  else if (this.plugin != null)
    {
      this.embed_function = embed_video_as_embed;
      if ((this.plugin.name.indexOf("LiveVideo") >= 0) ||
	  (this.plugin.name.indexOf("NPAVI") >= 0))
	{
	  this.plugin_type = "LiveVideo";
	  this.embed_type = VIDEO_PLUGIN;
	  this.control_panel_height = 0; // No control panel.
	}
      else if (this.plugin.name.indexOf("QuickTime") >= 0)
	{
	  this.plugin_type = "QuickTime";
	  this.embed_type = VIDEO_PLUGIN;
	  this.control_panel_height = 24;
	}
      else if (this.plugin.name.indexOf("XAnim") >= 0)
	{
	  this.plugin_type = "XAnim";
	  this.embed_type = VIDEO_PLUGIN;
	  this.control_panel_height = 0;
	  if (this.plugin.name.indexOf("LiveConnect") >= 0)
	    {
	      this.plugin_type = "XAnim LiveConnect";
	      this.embed_function = embed_video_as_xalc;
	      this.control_panel_height = 24;
	      // (The control panel is an applet and not part of the plugin)
	    }
	}
      else if (this.plugin.name.indexOf("RealVideo") >= 0)
	{
	  this.plugin_type = "RealVideo";
	  this.embed_type = VIDEO_PLUGIN;
	  this.control_panel_height = 140; // Here it's about 129....
	}
      else if (this.plugin.name.indexOf("Vivo") >= 0)
	{
	  this.plugin_type = "Vivo";
	  this.embed_type = VIDEO_PLUGIN;
	  this.control_panel_height = 140; // I'm guessing.
	}
      else
	{
	  // It's some other plugin that I don't know about.
	  this.plugin_type = "OtherPlugin";
	  // Leave as hyperlink.
	  this.embed_function = embed_video_as_hyperlink;
	}
    }
}

var video_info = new Array();
video_info[VIDEO_QUICKTIME] =
  new VideoPlayerInfo(VIDEO_QUICKTIME, "video/quicktime", "QuickTime Video",
		      ".mov", ".qt");
video_info[VIDEO_AVI] =
  new VideoPlayerInfo(VIDEO_AVI, "video/x-msvideo", "AVI Video",
		      ".avi");
video_info[VIDEO_MPEG] =
  new VideoPlayerInfo(VIDEO_MPEG, "video/mpeg", "MPEG Video",
		      ".mpeg", ".mpg", ".mpe");
video_info[VIDEO_FLIC] =
  new VideoPlayerInfo(VIDEO_FLIC, "video/x-autodesk", "Flic Video",
		      ".fli", ".flc", ".flic");
video_info[VIDEO_REALVIDEO] =
  new VideoPlayerInfo(VIDEO_REALVIDEO, "video/x-pn-realaudio", "RealVideo",
		      ".rm", ".ra", ".ram");
video_info[VIDEO_RVPLUGIN] =
  new VideoPlayerInfo(VIDEO_RVPLUGIN, "video/x-pn-realaudio-plugin",
		      "RealVideo (Plugin)", ".rpm");
video_info[VIDEO_VIVO] =
  new VideoPlayerInfo(VIDEO_VIVO, "video/x-vivo", "Vivo Video",
		      ".vivo", ".viv");
video_info[VIDEO_SGI] =
  new VideoPlayerInfo(VIDEO_SGI, "video/x-sgi-movie", "SGI Movie",
		      ".mv", ".movi", ".movie");
video_info[VIDEO_GIF89A] =
  new VideoPlayerInfo(VIDEO_GIF89A, "image/gif", "GIF89a Animation",
		      ".gif");
function get_video_info(filename)
{
  var i, j;
  var ind;
  var ai, ais;
  ind = null;
  var found = false;
  for (i = 0; !found && (i < VIDEO_TYPE_COUNT); i++)
    {
      ai = video_info[i];
      ais = ai.suffix;
      for (j = 0; !found && (j < ai.nsuffixes); j++)
	{
	  if (filename.lastIndexOf(ais[j]) >= 0)
	    {
	      ind = ai;
	      found = true;
	    }
	}
    }
  return ind;
}

function embed_video_as_hyperlink(v, w, h, n)
{
  document.writeln('<a href="' + v + '">' + video_info[n].description
		   + '</a>');
}

// This is a generic <embed> command.
function embed_video_as_embed(v, w, h, n)
{
  document.writeln('<embed type="' + video_info[n].mimetype + '" ');
  document.writeln('src="' + v + '" ');
  document.writeln('width="' + w + '" height="' +
		   (h + video_info[n].control_panel_height) + '" ');
  document.writeln('autoplay="no" autostart="no">');
  document.writeln('<noembed>');
  embed_video_as_hyperlink(v, w, h, n);
  document.writeln('</noembed>');
  document.writeln('</embed>');
}

function embed_video_as_gif89a(v, w, h, n)
{
  document.writeln('<img src="' + v + '" ');
  document.writeln('width="' + w + '" height="' + h + '" />');
}

function embed_video_as_dynsrc(v, w, h, n)
{
  document.writeln('<img dynsrc="' + v + '" loop="infinite" ');
  document.writeln('width="' + w + '" height="' +
		   (h + video_info[n].control_panel_height) + '" />');
}

// The only really fancy embed is the one for XAnim LiveConnect, which
// requires an applet to control the plugin.  The applet is
// distributed with the plugin, so it will be available.
var xalc_count_ = 0;
function embed_video_as_xalc(v, w, h, n)
{
  var xalc_name = "XAnimLiveConnect" + xalc_count_++;
  document.writeln('<table border="0" cellspacing="0" cellpadding="0">');
  document.writeln('<tr><td align="center">');
  document.writeln('<embed type="' + video_info[n].mimetype + '" ');
  document.writeln('src="' + v + '" ');
  document.writeln('width="' + w + '" height="' + h + '" ');
  document.writeln('name="' + xalc_name + '">');
  document.writeln('</embed></td></tr>');
  document.writeln('<tr><td align="center">');
  document.writeln('<applet mayscript="true" width="' + w +
		   '" height="' + video_info[n].control_panel_height +
		   '" code="XanimPluginApplet.class">');
  document.writeln('<param name="xanimpluginname" value="' +
		   xalc_name + '"></param>');
  embed_video_as_hyperlink(v, w, h, n);
  document.writeln('</applet>');
  document.writeln('</td></tr></table>');
}

function embedVideo(width, height)
{
  var i = 0;
  var ai;
  // We loop through the file names in order, choosing the best one.
  // The criteria for "best" are:
  // Most important: embed type.  (VIDEO_PLUGIN is best.)
  // Second most important: machine preference.  (true is best.)
  // Least important: position in list.  (earlier is better.)
  // The variable best_index is the index of the best file we've found so far.
  // It starts out as 2 so that we will at least get
  // a link to that first file no matter what happens.
  // We update best_index whenever we set preferred or increase etype.
  // When we increase etype, we clear preferred if appropriate.
  var preferred = false;
  var etype = VIDEO_UNKNOWN;
  var best_index = 2;
  var best_ai = null;

  if (arguments.length < 3)
    {
      return; // Screw it.
    }
  else if (getVideoDispatcher() == null)
    {
      //best_ai = get_video_info(arguments[2]);
      for (i = 2; i < arguments.length; i++)
	{
	  ai = get_video_info(arguments[i]);
	  if (ai != null && ai.embed_type > etype)
	    {
	      best_index = i;
	      best_ai = ai;
	      etype = ai.embed_type;
	      preferred = false;
	    }
	  if (ai != null && ai.embed_type == etype) // might have just been set
	    {
	      if (!preferred && ai.is_preferred_plugin)
		{
		  best_index = i;
		  best_ai = ai;
		  preferred = true;
		  //alert(ai.description);
		}
	    }
	}
      // At this point, we have figured out the "best" video to embed.
    }
  else
    {
      document.write('<font color="red">Error in video dispatcher!</font>');
    }

//  alert("best index: " + best_index + "; file: " +
//	arguments[best_index] + "; embed: " + best_ai.plugin_type);

  // We know what the "best" video to embed is.
  // But if its type is VIDEO_LINK, we instead want to put in
  // hyperlinks to all the files.
  if (etype > VIDEO_LINK)
    {
      best_ai.embed_function(arguments[best_index], width, height,
			     best_ai.type);
    }
  else if (arguments.length == 3)
    {
      embed_video_as_hyperlink(arguments[2], width, height, best_ai.type);
    }
  else
    {
      document.writeln('<table border="0" cellspacing="0">' +
		       '<tr><td align="left"><ul>');
      for (i = 2; i < arguments.length; i++)
	{
	  document.writeln('<li>');
	  embed_video_as_hyperlink(arguments[i], width, height,
				   get_video_info(arguments[i]).type);
	  document.writeln('</li>');
	}
      document.writeln('</ul></tr></td></table>');
    }
}


// OK, now for popup video.  (Not the VH1 type!)
// Our requirements are slightly different:
// 1. We only want one link.
// 2. We don't particularly care if there is a plugin.
//  (We hope that everything can be played somehow.)
// 3. If there is a plugin, then we have to open a new browser window
// a la popUpAnswer().
//
// So our criteria are:
// 1. The browser can handle the video (x.navigator_can_handle == true)
// 2. Chronological order.

function linkVideo(width, height)
{
  var i;
  var vi;
  // We are currently ignoring issues of preference.
  var best_i;
  var best_vi;
  var p = false;
  var pp = false;
  for (i = 2; i < arguments.length; i++)
    {
      vi = get_video_info(arguments[i]);
      if (vi != null && vi.navigator_can_handle) // Ding!
	{
	  if (vi.plugin == null)
	    {
	      //alert("Hyperlink");
	      document.write("(");
	      embed_video_as_hyperlink(arguments[i], width, height, vi.type);
	      document.write(")");
	    }
	  else
	    {
	      //alert("Plugin");
	      make_popup_link(arguments[i], width, height, vi);
	    }
	  break;
	}
    }
}

function make_popup_link(filename, width, height, vi)
{
  document.writeln('(<a href="javascript:' +
		   "window.open('" + filename + "', 'VideoWindow', " +
		   "'width=" + (width+10) + ",height=" +
		   (height + vi.control_panel_height + 10) +
		   ",resizable=yes,status=yes," +
		   "menubar=yes,scrollbars=no').focus();" +
		   '" onmouseover="' +
		   "window.status='Pop up " + vi.description + "';" +
		   '">' + vi.description + '</a>)');
}

// Here is a method to pop up a video, to be used as an href or handler.
function popupVideo(width, height)
{
  var i;
  var vi;
  var best_i;
  var best_vi;
  var p = false;
  var pp = false;
  for (i = 2; i < arguments.length; i++)
    {
      vi = get_video_info(arguments[i]);
      if (vi != null && vi.navigator_can_handle) // Ding!
	{
	  if (vi.plugin == null)
	    {
	      document.location = arguments[i];
	    }
	  else
	    {
	      window.open(arguments[i], 'VideoWindow',
			  ("width=" + (width+10) + ",height=" +
			   (height + vi.control_panel_height + 10) +
			   ",resizable=yes,status=yes," +
			   "menubar=yes,scrollbars=no")).focus();
	    }
	  break;
	}
    }
}


// -----------------

// End Video Dispatcher
// -----------------------

// (6) Audio Dispatcher

// This provides the method audio() which takes any number of URLs as
// arguments.  The code puts a clickable icon (sort of a hyperlink)
// into the text.  When the user clicks on the icon, the sound will
// play somehow.  Exactly how is not your concern.  The browser tries
// to pick which file or format is best for your machine.  Then, all
// other things being equal, the browser will choose the first usable
// file.

// This is for short clips.  Do not assume that anything will happen
// except that the sound is played once without any control windows
// popping up or anything like that.  In fact, I am trying to arrange
// it so that nothing ever pops up.

// Eventually, this will try to use the VideoDispatcher Applet to play
// the sound as an AudioClip, with the Java Media Framework if
// possible.  But it will work without an Applet.

// Questions or problems to Mike Malak.  Flames to /dev/null.

// -----------------

var sound_icon_file = module_root() + "shared/graphics/sound.gif";

/** This returns the object (usually an Applet) that acts as the
    document's audio dispatcher.  If the audio dispatcher is null,
    then the Javascript must handle dispatching.  (It's not too
    complicated, but an Applet has some slightly better ways.) **/
function getAudioDispatcher()
{
  // OK, so I haven't written an AudioDispatcher yet.
  return null;
}

var AUDIO_UNKNOWN = -1;

// We will support a few audio types:
//   Basic (.au)
//   Wave (.wav)
//   AIFF (.aif, .aiff, .aifc)
//   MIDI (.mid, .midi)
//   QuickTime(.qt, .mov)
//   AVI (.avi)
//   MPEG (.mpeg, .mpe, .mpg)
//   MPEG-2 (.mp2)
//   MPEG-3 (.mp3)
//   Rich Music Format (.rmf)
//   Real Audio (.ra, .ram, .rm)
//   Real Audio Plugin (.rpm)
// These are in no particular order.
var AUDIO_TYPE_COUNT = 12;

var AUDIO_BASIC = 0;
var AUDIO_WAVE = 1;
var AUDIO_AIFF = 2;
var AUDIO_MIDI = 3;
var AUDIO_QUICKTIME = 4;
var AUDIO_AVI = 5;
var AUDIO_MPEG = 6;
var AUDIO_MPEG2 = 7;
var AUDIO_MPEG3 = 8;
var AUDIO_RMF = 9;
var AUDIO_REALAUDIO = 10;
var AUDIO_RAPLUGIN = 11;

// There are four ways to embed audio: using a scriptable invisible
// embedded plugin, using an applet, using a full-screen plugin
// (yecch), and just using a hyperlink (this includes visible
// non-full-screen plugins).
var AUDIO_EMBED_COUNT = 4;

var AUDIO_PLUGIN = 3;
var AUDIO_APPLET = 2;
var AUDIO_FULLSCREEN = 1;
var AUDIO_LINK = 0;

// Here is an object that encapsulates what we know about
// the browser and its audio plugins.
function AudioPlayerInfo(_type, _mimetype) // _ext1, _ext2, ....
{
  var i;
  var _ext;
  this.type = _type;
  this.mimetype = _mimetype;
  this.nsuffixes = arguments.length - 2;
  this.suffix = new Array(this.nsuffixes);
  for (i = 0; i < this.nsuffixes; i++)
    {
      _ext = arguments[i+2];
      if (_ext.indexOf(".") == 0)
	this.suffix[i] = _ext;
      else
	this.suffix[1] = "." + _ext;
    }

  this.is_preferred = false;
  var preferred_type;
  if (whatStyle.indexOf("Win") >= 0)
    {
      preferred_type = AUDIO_WAVE;
    }
  else if (whatStyle.indexOf("Mac") >= 0)
    {
      preferred_type = AUDIO_AIFF;
    }
  else
    {
      preferred_type = AUDIO_BASIC;
    }

  if (preferred_type == this.type)
    this.is_preferred = true;

  this.embed_type = AUDIO_LINK;
  if ((this.mimetype == "audio/basic") || (this.suffix == ".au"))
    this.embed_type = AUDIO_APPLET;

  this.plugin = null;
  if (whatStyle.indexOf("Net") >= 0) // Only Netscape has plugin info.
    {
      if ((navigator.mimeTypes[this.mimetype] == null) ||
	  (navigator.mimeTypes[this.mimetype] == "undefined"))
	{
	  this.plugin = null;
	}
      else
	{
	  this.plugin = navigator.mimeTypes[this.mimetype].enabledPlugin;
	  if (this.plugin == "undefined") this.plugin = null;
	}
    }
  this.plugin_type = null; // Short name of the plugin.

  // Some plugins are just not very useful for our purposes.
  // The QuickTime plugin is not scriptable, so it must be full-screen
  // (we'll put it into a popup window),
  // and the Xanim plugin can't behave the way we want.
  // So far, the only acceptable one is LiveAudio.
  // Actually, even LiveAudio isn't quite right.
  if (this.plugin != null)
    {
      if (this.plugin.name.indexOf("LiveAudio") >= 0)
	{
	  this.plugin_type = "LiveAudio";
	  // LiveAudio is OK as a hyperlink, so don't upgrade to FULLSCREEN.
	}
      else if (this.plugin.name.indexOf("QuickTime") >= 0)
	{
	  this.plugin_type = "QuickTime";
	  // This sucks, because QT is full-screen.
	  // Maybe we don't need it, though.
	  if (this.embed_type < AUDIO_FULLSCREEN)
	    this.embed_type = AUDIO_FULLSCREEN;
	}
      else if (this.plugin.name.indexOf("XAnim") >= 0)
	{
	  this.plugin_type = "XAnim";
	  // Leave this as a hyperlink; don't upgrade to FULLSCREEN.
	}
      else
	{
	  // It's some other plugin that I don't know about.
	  this.plugin_type = "OtherPlugin";
	  // Leave as hyperlink.
	}
    }
}

var audio_info = new Array();
audio_info[AUDIO_BASIC] =
  new AudioPlayerInfo(AUDIO_BASIC, "audio/basic", ".au", ".snd");
audio_info[AUDIO_WAVE] =
  new AudioPlayerInfo(AUDIO_WAVE, "audio/x-wav", ".wav");
audio_info[AUDIO_AIFF] =
  new AudioPlayerInfo(AUDIO_AIFF, "audio/x-aiff", ".aif", ".aiff", ".aifc");
audio_info[AUDIO_MIDI] =
  new AudioPlayerInfo(AUDIO_MIDI, "audio/x-midi", ".mid", ".midi");
audio_info[AUDIO_QUICKTIME] =
  new AudioPlayerInfo(AUDIO_QUICKTIME, "video/quicktime", ".mov", ".qt");
audio_info[AUDIO_AVI] =
  new AudioPlayerInfo(AUDIO_AVI, "video/x-msvideo", ".avi");
audio_info[AUDIO_MPEG] =
  new AudioPlayerInfo(AUDIO_MPEG, "video/mpeg", ".mpeg", ".mpg", ".mpe");
audio_info[AUDIO_MPEG2] =
  new AudioPlayerInfo(AUDIO_MPEG2, "audio/x-mpeg2", ".mp2");
audio_info[AUDIO_MPEG3] =
  new AudioPlayerInfo(AUDIO_MPEG3, "audio/x-mpeg3", ".mp3");
audio_info[AUDIO_RMF] =
  new AudioPlayerInfo(AUDIO_RMF, "audio/rmf", ".rmf");
audio_info[AUDIO_REALAUDIO] =
  new AudioPlayerInfo(AUDIO_REALAUDIO, "audio/x-pn-realaudio",
		      ".rm", ".ra", ".ram");
audio_info[AUDIO_RAPLUGIN] =
  new AudioPlayerInfo(AUDIO_RAPLUGIN, "audio/x-pn-realaudio-plugin", ".rpm");

function get_audio_info(filename)
{
  var i, j;
  var ind;
  var ai, ais;
  ind = null;
  var found = false;
  for (i = AUDIO_BASIC; !found && (i < AUDIO_TYPE_COUNT); i++)
    {
      ai = audio_info[i];
      ais = ai.suffix;
      for (j = 0; !found && (j < ai.nsuffixes); j++)
	{
	  if (filename.lastIndexOf(ais[j]) >= 0)
	    {
	      ind = ai;
	      found = true;
	    }
	}
    }
  return ind;
}

// Here's how to embed an audio file with an invisible LiveAudio plugin:
function embed_invisible_LiveAudio(s, n)
{
  //alert("embed_invisible_LiveAudio(" + s + ", " + n + ")");
  document.writeln('<embed src="' + s + '"');
  document.writeln(' width="0" height="0" autostart="false" loop="false"');
  if (n != null) document.writeln(' name="' + n + '"');
  document.writeln(' controls="playbutton" hidden="true"></embed>');
}

// Here's how to place a clickable icon and an invisible LiveAudio
// so that clicking on the icon plays the audio.
var liveaudio_count = 0;
function embed_audio_with_LiveAudio(s)
{
  //alert("embed_audio_with_LiveAudio(" + s + ")");
  var name = "LiveAudio" + liveaudio_count++;
  embed_invisible_LiveAudio(s, name);
  document.writeln('<a href="' + s + '"');
  document.writeln(' onClick="document.' + name + '.play(); return true;"');
//  document.writeln(' onMouseOver="status=\'Play ' + s + '\'; return true;"');
//  document.writeln(' onMouseOut="status=\'\'; return true;">');
  document.writeln('><img src="' + sound_icon_file + '"');
  document.writeln(' border="1" width="16" height="16" align="absbottom"');
  document.writeln(' /></a>');
}

// Here is the general way to embed audio with a plugin.
// It's really easy right now, since the only good plugin is LiveAudio.
function embed_audio_with_plugin(s)
{
  //alert("embed_audio_with_plugin(" + s + ")");
  embed_audio_with_LiveAudio(s);
}

// Here's how to place a clickable icon that is just a hyperlink to
// the audio file.
function embed_audio_with_hyperlink(s)
{
  document.write('<a href="' + s + '"><img src="' +
		 sound_icon_file + '" border="1" width="16" ' +
		 'height="16" align="absbottom" /></a>');
}

// Here's how to place an audio applet that when clicked upon, will play
// the audio file.
function embed_audio_applet(s, n)
{
  document.writeln('<applet');
  document.writeln(' codebase="' + module_root() + 'shared/code/links/"');
  if (n != null) document.writeln(' name="' + n + '"');
  document.writeln(' width="18" height="18"');
  document.writeln(' archive="audioapplet.jar,appletstuff.jar"');
  document.writeln(' code="links.applet.audio.AudioApplet.class"');
  document.writeln(' mayscript="true" align="absbottom">');
  document.writeln('<param name="src" value="' + s + '"></param>');
  //document.writeln('<param name="bgcolor" value="' + 
  //                 document.bgColor + '"></param>');
  //alert("bgcolor: " + document.bgColor);
  //document.writeln('<param name="linkcolor" value="' + 
  //                 document.linkColor + '"></param>');
  //alert("linkcolor: " + document.linkColor);
  //document.writeln('<param name="alinkcolor" value="' + 
  //                 document.alinkColor + '"></param>');
  //alert("alinkcolor: " + document.linkColor);
  //document.writeln('<param name="vlinkcolor" value="' + 
  //                 document.vlinkColor + '"></param>');
  //alert("vlinkcolor: " + document.linkColor);
  document.writeln('</applet>');
}

// The AudioApplet can be scripted to play other sounds.
// So we only need one on a page.
// The first call to this method creates the applet;
// subsequent calls create calls to that applet.
// Actually, this doesn't work...device grab...so the name of the
// applet is always reset to null; this means that every file gets its
// own applet.  Oh, well.
var audioapplet_count = 0;
var audioapplet_name = null;
function embed_audio_with_applet(s)
{
  if (audioapplet_name == null) // First call
    {
      audioapplet_name = "audioapplet" + audioapplet_count++;
      embed_audio_applet(s, audioapplet_name);
      audioapplet_name = null;
    }
  else // Subsequent call
    {
      document.writeln('<a href="' + s + '"');
      document.writeln(' onClick="document.' + audioapplet_name +
		       '.play(\'' + document.location + '\', \'' +
		       s + '\'); return true;"');
      document.writeln('><img src="' + sound_icon_file + '"');
      document.writeln(' border="1" width="16"' +
		       ' height="16" align="absbottom" /></a>');
    }
}

// Here is a way to deal with the case that the enabledPlugin is a
// full-screen viewer like QuickTime.  We have the link pop up a
// browser window containing just that file.
function embed_audio_with_fullscreen(s)
{
  document.writeln('<a href="' + s + '" target="_blank"');
//  document.writeln(' onClick="embed_audio_as_popup(\'' + s + '\');' +
//		   ' return true;"');
  document.writeln('><img src="' + sound_icon_file + '"');
  document.writeln(' border="1" width="16"' +
		   ' height="16" align="absbottom" /></a>');
}

function embed_audio_as_popup(s)
{
  window.open(s, "Audio", "width=320, height=240");
//  foo(", resizable=no," +
//	      " status=no, menubar=yes, scrollbars=no," +
//	      " toolbar=no, location=no, directories=no);
}
// Now that we have four ways to embed audio, and a way to assign
// audio info to a filename, let's make a generic embed method:
function embed_audio(s)
{
  var ai;
  ai = get_audio_info(s);
  if (ai == null)
    {
      embed_audio_with_hyperlink(s);
    }
  else if (ai.embed_type == AUDIO_APPLET)
    {
      embed_audio_with_applet(s);
    }
  else if (ai.embed_type == AUDIO_PLUGIN)
    {
      embed_audio_with_plugin(s);
    }
  else if (ai.embed_type == AUDIO_FULLSCREEN)
    {
      embed_audio_with_fullscreen(s);
    }
  else
    {
      embed_audio_with_hyperlink(s);
    }
}

/** Use the audio method to place a clickable icon (sort of a
    hyperlink) into the text.  When the user clicks on the icon, the
    sound will play somehow.  It is like embedVideo in that it can
    take more than one file name. **/
function audio()
{
  var i = 0;
  var ai;
  // We loop through the file names in order, choosing the best one.
  // The criteria for "best" are:
  // Most important: embed type.  (AUDIO_PLUGIN is best.)
  // Second most important: machine preference.  (true is best.)
  // Least important: position in list.  (earlier is better.)
  // The variable best_index is the index of the best file we've found so far.
  // It starts out as 0 so that we will at least get
  // a link to that first file no matter what happens.
  // We update best_index whenever we set preferred or increase etype.
  // When we increase etype, we clear preferred if appropriate.
  var preferred = false;
  var etype = AUDIO_UNKNOWN;
  var best_index = 0;

  if (arguments.length == 0) return; // Screw it.
  else if (getAudioDispatcher() == null)
    {
      for (i = 0; i < arguments.length; i++)
	{
	  ai = get_audio_info(arguments[i]);
	  if (ai.embed_type > etype)
	    {
	      best_index = i;
	      etype = ai.embed_type;
	      preferred = false;
	    }
	  if (ai.embed_type == etype) // It might have just been set
	    {
	      if (!preferred && ai.is_preferred)
		{
		  best_index = i;
		  preferred = true;
		}
	    }
	}
      embed_audio(arguments[best_index]);
    }
  else
    {
      document.write('<font color="red">Error in audio dispatcher!</font>');
    }
  return;
}

// End Audio Dispatcher
// -----------------------


// (7) Tiny Quiz

// A Tiny Quiz presents a question and a set of radio buttons with
// possible answers, and a text area.  When the reader clicks one of
// the radio buttons, a piece of text appears in the text area to
// provide instant feedback.

var tiny_quiz_number = 0;
var tiny_quiz_name;
var tiny_quiz_area;
var tiny_quiz_radio;
var tiny_quiz_rows;
var tiny_quiz_cols;

function beginTinyQuiz(question, optional_rows, optional_columns)
{
  if (optional_rows == null) tiny_quiz_rows = 5;
  else tiny_quiz_rows = optional_rows;
  tiny_quiz_cols = optional_columns;
  tiny_quiz_name = "tinyquiz" + tiny_quiz_number++;
  tiny_quiz_area = "ta" + tiny_quiz_name;
  tiny_quiz_radio = "rb" + tiny_quiz_name;
  document.writeln('<form name="' + tiny_quiz_name +'">');
  document.writeln('<table border="0"><tr><td colspan="2" align="center">');
  document.writeln(question);
  document.writeln('</td></tr>');
}
function addTinyQuizChoice(label, feedback_text)
{
  document.writeln('<tr><td align="right"><input type="radio" name="' +
		   tiny_quiz_radio + '"');
  document.writeln(' onclick="' + tiny_quiz_area +  '.value=' + "'" +
		   feedback_text + "'" + '; return true;">');
  document.writeln('</td><td>' + label + '</td></tr>');
}
function endTinyQuiz()
{
  document.writeln('<tr><td colspan="2" align="center">');
  document.writeln('<textarea name="' + tiny_quiz_area + '"');
  if (tiny_quiz_cols != null)
    document.writeln(' cols="' + tiny_quiz_cols + '"');
  document.writeln(' wrap="virtual" rows="' + tiny_quiz_rows + '">');
  document.writeln('</textarea></td></tr></table></form>');
}

// End Tiny Quiz
// ----------------------------------------------


// (8) TeX/HTML selector

// This is for anyone (anyone at all?) who uses TeX.
// In case you have both TeX and HTML pages of the same content, you
// can use this function to pick one.  It returns the first argument
// that is either an HTML file or a TeX file if TeX files can be
// displayed.  This means that chooseTeXorHTML("foo.html", "foo.tex")
// will always return "foo.html" even if the student's browser has IBM
// techexplorer installed.  So put the file names in the opposite
// order: chooseTeXorHTML("foo.tex", "foo.html") is what you want.
// The function will return null if none of its arguments is an HTML
// file name or a usable TeX file name, so don't use it with other
// file types or with no arguments.

// A good use for this function is in conjunction with the navigation
// bar maker.

// First, some convenience functions.
function ends_with(s, e)
{
  return (s.lastIndexOf(e) == s.length - e.length);
}
function is_tex(t)
{
  return ((t != null) && (ends_with(t, ".tex") ||
			  ends_with(t, ".tcx") ||
			  ends_with(t, ".ltx") ||
			  ends_with(t, ".latex")));
}
function is_html(h)
{
  return ((h != null) && (ends_with(h, ".html") ||
			  ends_with(h, ".htm")));
}

// This function examines its string-URL arguments in order, and
// returns the first one that fulfills either of the following
// criteria:
// 1.  It is an HTML file (ends with .htm or .html)
//  or
// 2.  It is a TeX file (.tex, .tcx, .ltx, .latex) and IBM
// techexplorer is installed and enabled (has_techexplorer is true).
//
// If no argument meets either criterion, the function returns null.

function chooseTeXorHTML()
{
  var i;
  for (i = 0; i < arguments.length; i++)
    {
      if ((has_techexplorer && is_tex(arguments[i])) ||
	  is_html(arguments[i]))
	{
	  return(arguments[i]);
	}
    }
  return null;
}

// This creates a hyperlink to either the TeX file or the HTML file.
function linkTeXorHTML(link_text, tex_file, html_file,
		       optional_target, optional_class)
{
  document.write('<a href="' + chooseTeXorHTML(tex_file, html_file));
  if (optional_target != null)
    {
      document.write('" target="' + optional_target);
    }
  if (optional_class != null)
    {
      document.write('" class="' + optional_class);
    }
  document.write('">' + link_text + '</a>');
}

// End TeX/HTML selector
// ----------------------------------------------

// (9) Mathematician biography macro

// This creates a hyperlink to a biography of a mathematician.
// We are using the collection at St. Andrew's University in Scotland.
// (I understand that their golf team is good, too. :-)
// |name| is the mathematician's name as it appears in the text, and
// |htmlname| is the name from the HTML file (check if you're not sure).
// Usually you can just use the same name for both.

function bio(name, htmlname)
{
  if (htmlname == null) htmlname = name;
  document.write('(<a class="external" ' +
                 'href="http://www-groups.dcs.st-and.ac.uk/' +
                 '~history/Mathematicians/' + htmlname + '.html">' +
                 'Biography of ' + name + '</a>)');
}

// End biography macro
// ----------------------------------------------------

// (10) GIF89a Animation/Still Switcher

// This allows you to embed a GIF89a animation along with a
// similarly-sized still image, which could be the first frame of the
// animation, for example.  It provides a start and stop button that
// actually switch the img.src between the animation and the still.
// It doesn't actually pause the animation.  Now, it might be that the
// still image has nothing to do with the "current" frame of the
// animation, so be careful that the still isn't going to fool anyone
// into thinking that it is the current frame.

// We might have more than one; make sure they have unique names.
var anim_count = 0;
var anim_name = null;
var anim_array = new Array();

// This is actually an object constructor.
// the width and height are optional, but, as always with images,
// they are a good idea.
function GifAnimation(anim_src, still_src, width, height)
{
  if (width != null && height != null)
    {
      this.anim_image = new Image(width, height);
      this.still_image = new Image(width, height);
      this.width = width;
      this.height = height;
    }
  else
    {
      this.anim_image = new Image();
      this.still_image = new Image();
      this.width = null;
      this.height = null;
    }
  this.anim_image.src = anim_src;
  this.still_image.src = still_src;

  // varnum is the anim's index in the array.
  this.varnum = anim_count++;
  // name is the NAME of the IMG.
  this.name = "gifanimation" + this.varnum;

  anim_array[this.varnum] = this;

  // Some of the member methods have simple names that could conflict
  // with other methods' names.  So their external names are more
  // complicated.
  this.stop = stop_anim;
  this.start = start_anim;
  this.draw = draw_anim;
  this.show = draw_table;

  // These aren't really for public consumption.
  this.draw_start_button = draw_start_button;
  this.draw_stop_button = draw_stop_button;
  this.draw_form = draw_form;
}

// Isn't it good that the images can be gotten by name?
// Here, we replace the animated image with the still.
function stop_anim()
{
  document.images[this.name].src = this.still_image.src;
  return true;
}

// Here, we replace the still image with the animation.
function start_anim()
{
  document.images[this.name].src = this.anim_image.src;
  return true;
}

// This writes the HTML for the image.
// It draws the still image rather than the animation, so as not to
// annoy people.
function draw_anim()
{
  document.write('<img src="' + this.still_image.src +
		 '" name="' + this.name + '"');
  if (this.width != null) document.write(' width="' + this.width + '"');
  if (this.height != null) document.write(' height="' + this.height + '"');
  document.write('></img>');
}

// Here is why I introduced varnum.  (and vailey.)  The onclick
// handler has to call a member method of the GifAnimation object.
// Since draw_start_button() will also be a member method, it doesn't
// know what variable name the animation will be given.  We could have
// the constructor make up a variable name and assign to it via an
// eval command, but the use of the animation array and varnum seemed
// a lot clearer.
function draw_start_button()
{
  document.write('<input type="button" value="start" onclick="anim_array[' +
		 this.varnum + '].start();"></input>');
}

function draw_stop_button()
{
  document.write('<input type="button" value="stop" onclick="anim_array[' +
		 this.varnum + '].stop();"></input>');
}

function draw_form()
{
  document.writeln('<form name="' + this.name + '_form">');
  this.draw_start_button();
  this.draw_stop_button();
  document.writeln('</form>');
}

function draw_table()
{
  document.writeln('<table border="0"><tr><td align="center">');
  this.draw();
  document.writeln('</td></tr><tr><td align="center">');
  this.draw_form();
  document.writeln('</td></tr></table>');
}

// This doesn't make the buttons, but it does return a reference to
// the animation object, in case you want to make your own buttons or
// something.
function makeGifAnimation(anim_src, still_src, width, height)
{
  var ga = new GifAnimation(anim_src, still_src, width, height);
  ga.draw();
  return ga;
}

// Here is the real public method, which puts in the animation and its
// control buttons.  You don't need to do anything else.
function embedGifAnimation(anim_src, still_src, width, height)
{
  var ga = new GifAnimation(anim_src, still_src, width, height);
  ga.show();
}

// Use this if you want the animation to start playing immedately.
function embedAndStartGifAnimation(anim_src, still_src, width, height)
{
  var ga = new GifAnimation(anim_src, still_src, width, height);
  ga.show();
  ga.start();
}

// End Animation switcher

// -----------------------------------

// 11. Slide Show.

// The easy way to use this is with
// embedSlideShow(width, height, img1, img2, img3....)

var slideshow_count = 0;
var slideshow_name = null;
var slideshow_array = new Array();

function SlideShow(argument_array)
{
  var i;

  this.width = argument_array[0];
  this.height = argument_array[1];
  this.slideshow_image = new Image(this.width, this.height);

  this.slide_name = new Array();
  this.nslides = argument_array.length - 2;

  for (i = 0; i < this.nslides; i++)
    this.slide_name[i] = argument_array[i + 2];

  this.current_slide = 0;
  this.slideshow_image.src = this.slide_name[this.current_slide];

  this.varnum = slideshow_count++;
  this.name = "slideshow" + this.varnum;

  slideshow_array[this.varnum] = this;

  this.advance = advance_slideshow;
  this.retreat = retreat_slideshow;

  this.draw = draw_slideshow;
  this.redisplay = redisplay_slideshow;
  this.showstatus = status_slideshow;
}

function advance_slideshow()
{
  this.current_slide++;
  if (this.current_slide >= this.nslides)
    {
      this.current_slide -= this.nslides;
    }
  this.redisplay();
}

function retreat_slideshow()
{
  this.current_slide--;
  if (this.current_slide < 0)
    {
      this.current_slide += this.nslides;
    }
  this.redisplay();
}

function redisplay_slideshow()
{
  document.images[this.name].src = this.slide_name[this.current_slide];
}

function status_slideshow()
{
  var s;
  if (this.current_slide == this.nslides - 1)
    {
      s = "Click to go back to first frame.";
    }
  else
    {
      s = "Click to advance slide show.";
    }
  window.status = s;
  return true;
}

function draw_slideshow()
{
  document.write('<a href="javascript:slideshow_array[' + this.varnum +
		 '].advance();" onmouseover="slideshow_array[' +
		 this.varnum + '].showstatus();">');
  document.write('<img src="' + this.slide_name[this.current_slide] +
		 '" border="0" name="' + this.name + '"');
  if (this.width != null) document.write(' width="' + this.width + '"');
  if (this.height != null) document.write(' height="' + this.height + '"');
  document.write('></img></a>');
}


function makeSlideShow()
{
  //alert(arguments);
  var ga = new SlideShow(arguments);
  ga.draw();
  return ga;
}

function embedSlideShow()
{
  (new SlideShow(arguments)).draw();
}

// Example:
// embedSlideShow(23, 53, "stop.gif", "yield.gif", "go.gif");

// End Slide Show
// -----------------------------------


// 12. Call update function in top frame:
//     THIS SHOULD BE THE LAST ENTRY IN THE SCRIPTS.JS FILE
//     ** it will be called iff the new navigation scheme is being used
//        (accommodates backwards compatibility with old navigation scheme **

if(typeof(top.moduleTitle)!="undefined")
    top.updateNavigation(location.href);


// -----------------------
// (13) Wrapper for Module Questions (think boxes)

// parameter:
// theSize: if "small", then question text printed as regular text.
//          if anything else (including null), defaults to text one size larger
// Assumption: 
//          "think.gif" is in the relative location "../shared/graphics/think.gif"
function beginQuestion( theSize ) {
	document.write("<IMG SRC=\"../../../shared/graphics/think.gif\">");
	if( theSize != null ) {
		if( theSize == "small" ) // can't call .equals on null object
			document.write("<FONT>");
		else		
			document.write("<FONT SIZE=+1>");
	}
	else		
		document.write("<FONT SIZE=+1>");
}

// parameters:
// urlLink: a URL at which the pop-up answer to this question can be found.
//          if null, no 'answer' text or link is displayed.
// text:    Default text is "Answer".
//          If you would like some other text, this is the parameter at which to do it.
//          Note that this text will be accepted *only* if a URL is also given.
// theWidth, theHeight:
//	    Optional parameters allowing explicit control over pop-up window sizings.
//	    (It is recommended that the HTML programmers do not use this, leaving 
//	    all pop-up windows a standard initial size).
// isManual:
//	    A *very* optional parameter allowing a user to pass something other than
// 	    a URL to popUpAnswer as the first parameter (urlLink).
//	    If the string "manual" is passed to isManual, the HTML programmer 
//	    may pass something like "javascript:someFunction('x')" as the first parameter.
function endQuestion( urlLink, text, theWidth, theHeight, isManual ) {
	document.write("</FONT>");
  	if (urlLink != null) {
		document.write( "" );

		if( isManual == "manual" ) {
	     			document.write("<A HREF=\"" + urlLink + "\">");
		}
		else {
			if( (theWidth != null) && (theHeight != null) ) {
     				document.write("<A HREF=\"javascript:popUpAnswersize(\'" + urlLink + "\',\'" );
				document.write( theWidth + "\',\'" + theHeight + "\')\">");
			}
			else {
	     			document.write("<A HREF=\"javascript:popUpAnswer(\'" + urlLink + "\')\">");
			}
		} // end outer else
		document.write( "<H5>" );
		if( text != null ) 
			document.write( "" + text );
		else 
			document.write( "Answer" );
		document.write("</H5></A>");	
	} // end if urlLink != null
	document.write("");
} // end endQuestion()


// -----------------------
// (14) Highlight Boxes

// parameters:
// headtext:	text to go at top of the box
// bodytext:	text in body of box
function highlightBox( headtext, bodytext ) {
	if( headtext == null && bodytext == null ) 
		return;

	document.write( "<TABLE ALIGN=\"center\" BGCOLOR=\"FFFF00\" CELLSPACING=0 BORDER=0 WIDTH=90%>" );
	document.write( "<TR><TD><U><H2 ALIGN=center>" );

	if( headtext != null )
		document.write( headtext );

	document.write( "</TD></TR><TR><TD><CENTER><STRONG>" );

	if( bodytext != null )
		document.write( bodytext );

	document.write( "</TD></TR></TABLE>" );
}

// -----------------------

// 15) beginContent() and endContent().

function beginContent( maintitle, subtitle ) {
	document.write( "<div class=\"normal\">" );
	document.write( "<BLOCKQUOTE>" );
	if( maintitle != null )
		document.write( "<H1>" + maintitle + "</H1>" );
	if( subtitle != null )
		document.write( "<H2>" + subtitle + "</H2>" );
}

function endContent() {
	document.write( "</DIV></blockquote><blockquote><H6><BR CLEAR=ALL>" );
	document.write( "Copyright &copy; " );

	if( top.copyrightYear == null )
		document.write( "1997-1999" );
	else	
		document.write( top.copyrightYear );

	document.write( " Rensselaer Polytechnic Institute. " );
	document.write( "All Rights Reserved.</H6></blockquote><BR></BODY></HTML>" );
}


// -----------------------

// (16) Example Boxes

// To be used when you want a graphic and accompanying text 
// to be set off from the page.

// parameters:	imagePath - the relative path to the image
//		theHeading - heavy text. Should be *very* brief!
//		theContent - body text.
function exampleBox( imagePath, theHeading, theContent ) {
	document.write( "<TABLE ALIGN=\"center\" BORDER=\"2\" BGCOLOR=\"FFFFAA\" CELLPADDING=\"7\">" );
	document.write( "<TR><TD>" );
	document.write( "<IMG SRC=\"" + imagePath + "\" ALIGN=\"LEFT\" HSPACE=\"8\"><BR>" );
	document.write( "<table><tr><td width=\"100\"><H2>" + theHeading + "</H2>" );
	document.write( theContent );
	document.write( "</td></tr></table></TD></TR></TABLE>" );
}

// -----------------------

// (17) Do Boxes

// To indicate that the module's user should interact ("do" something).
// Usually accompanies an applet.

function doBox( bodyCopy, theSize ) {
	if( bodyCopy != null ) {
		document.write( "<TABLE WIDTH=90% BORDER=0><TR><TD WIDTH=70 VALIGN=top>" );
		document.write( "<IMG SRC=\"../../../shared/graphics/do.gif\" WIDTH=50 HEIGHT=48></TD><TD>" );
		if( theSize == "small" )
			document.write( bodyCopy );	
		else
			document.write( "<FONT SIZE=+1>" + bodyCopy + "</FONT>" );
		document.write( "</TD></TR></TABLE>" );
	}
}

// -----------------------

// (18) deriveLink() and solveLink()

var imgNo = 0;	// used by genericSolveDeriveLink

// the function:
//		solveLink
// what it does:
//		generates a "solve link." Displays an equation and links to a solution page.
// parameters:
//		theURL: 	the relative URL of the page containing the derivation/solution. REQUIRED.
// 		formulaString:	the formula to be displayed via WebEQ. REQUIRED.
//		formulaWidth:	the desired width of the panel containing the equation. REQUIRED.
//		formulaHeight:	the desired height of the panel containing the equation. REQUIRED.
//		formulaNumber:	a number or string to be displayed as an association with this formula. OPTIONAL.
function solveLink( theURL, formulaString, formulaWidth, formulaHeight, formulaNumber, isText ) {
	genericSolveDeriveLink( theURL, formulaString, formulaWidth, formulaHeight, formulaNumber, "SOLVE!", isText );
}

// the function:
//		deriveLink
// what it does:
//		generates a "derive link." Displays an equation and links to a solution page.
// parameters:
//		theURL: 	the URL of the page containing the derivation/solution. REQUIRED.
// 		formulaString:	the formula to be displayed via WebEQ. REQUIRED.
//		formulaWidth:	the desired width of the panel containing the equation. REQUIRED.
//		formulaHeight:	the desired height of the panel containing the equation. REQUIRED.
//		formulaNumber:	a number or string to be displayed as an association with this formula. OPTIONAL.
function deriveLink( theURL, formulaString, formulaWidth, formulaHeight, formulaNumber, isText ) {
	genericSolveDeriveLink( theURL, formulaString, formulaWidth, formulaHeight, formulaNumber, "DERIVE!", isText );
}

// the function:
//		genericSolveDeriveLink
// what it does:
//		generates either a "solve link" or a "derive link", depending on string passed in
//              parameter whichKind. Possible values of whichKind: "DERIVE!" and "SOLVE!".
// limitation:
//		a maximum of 10 derive/solve links is allowed on any given page.
function genericSolveDeriveLink( theURL, formulaString, formulaWidth, formulaHeight, formulaNumber, whichKind, isText ) {
	if( theURL != null && formulaString != null 
	   && ((formulaWidth !=null && formulaHeight != null) || isText == "true") ) {
		document.write( "<TABLE ALIGN=CENTER CELLPADDING=0 CELLSPACING=0 WIDTH=95%>" );
		document.write( "<TR><TD WIDTH=20%></TD><TD WIDTH=35% ALIGN=LEFT>" );

		if( isText != "true" )
			display( formulaString, formulaWidth, formulaHeight );
		else
			document.write( formulaString );

		document.write( "</TD><TD WIDTH=15% ALIGN=CENTER>" );
		document.write( "<A HREF=\"javascript:popUpAnswersize(\'" );
		document.write(  theURL + "\',\'600\',\'480\')\" " );
		document.write( "onMouseOver=\"document.rollover_deriv" + imgNo +".src=deriv3.src\" " );
		document.write( "onMouseOut=\"document.rollover_deriv"+ imgNo +".src=deriv.src\">" );
		document.write( "<IMG " );
		document.write( "SRC=\"../../../shared/graphics/deriv.gif\" " );
		document.write( "NAME=\"rollover_deriv" + imgNo + "\" WIDTH=40 HEIGHT=40 BORDER=0 " ); 

		// need different names for each image instance in the document!!!!
		// (or else limited to 1 instance per page)

		if( whichKind == "DERIVE!" )				
			document.write( "ALT=\"Derivation\"" );
		else if( whichKind == "SOLVE!" )
			document.write( "ALT=\"Solve\"" );

		document.write( " VALIGN=TOP>" );

		// here is a kludgy solution that works,
		// but limits us to 10 links per page.

		switch( imgNo ) {
		case 0:
			document.rollover_deriv0.src=deriv.src;
			break;
		case 1:
			document.rollover_deriv1.src=deriv.src;
			break;
		case 2:
			document.rollover_deriv2.src=deriv.src;
			break;
		case 3:
			document.rollover_deriv3.src=deriv.src;
			break;
		case 4:
			document.rollover_deriv4.src=deriv.src;
			break;
		case 5:
			document.rollover_deriv5.src=deriv.src;
			break;
		case 6:
			document.rollover_deriv6.src=deriv.src;
			break;
		case 7:
			document.rollover_deriv7.src=deriv.src;
			break;
		case 8:
			document.rollover_deriv8.src=deriv.src;
			break;
		case 9:
			document.rollover_deriv9.src=deriv.src;
			break;
		default:
			document.write( "<H3>Maximum 10 derive/solve instances per page!!!!</H3>" );
		}

		document.write( "</A></TD><TD WIDTH=\"10%\" ALIGN=LEFT>" );
		document.write( "<FONT FACE=\"Arial, Verdana, Geneva\" SIZE=\"-1\"><STRONG>" );
		document.write( "<A HREF=\"javascript:popUpAnswersize(\'" );
		document.write( theURL + "\',\'600\',\'480\') \">" );
		document.write( whichKind + "</A></STRONG></FONT></TD>" );

		document.write( "<TD WIDTH=\"20%\" ALIGN=CENTER>" );
		if( formulaNumber != null ) {
			document.write( "(" + formulaNumber + ")" );
		}
		document.write( "</TD>" );
		document.write( "</TR></TABLE>" );
		imgNo++;  // set for next button instance
	}
	else { // forgot a parameter
		document.write( "<P ALIGN=CENTER><STRONG>" );
		document.write( "Incorrect usage. Correct usage is:<BR>" );
		if( whichKind == "DERIVE!" )
			document.write( "deriveLink( theURL, formulaString, formulaWidth, formulaHeight, formulaNumber )" );
		else if( whichKind == "SOLVE!" )
			document.write( "solveLink( theURL, formulaString, formulaWidth, formulaHeight, formulaNumber )" );
		document.write( "</STRONG></P>" );
	}
} // end genericSolveDeriveLink()


// -----------------------

// (19) Paper Boxes

// To indicate that the module's user is now expected to do something on paper.
// Includes text (passed in via bodyCopy) describing the task at hand.

function paperBox( bodyCopy, theSize ) {
	if( bodyCopy != null ) {
		document.write( "<TABLE WIDTH=90% BORDER=0><TR><TD WIDTH=70 VALIGN=top>" );
		document.write( "<IMG SRC=\"../../../shared/graphics/paper.gif\" WIDTH=57 HEIGHT=80></TD><TD>" );
		if( theSize == "small" )
			document.write( bodyCopy );	
		else
			document.write( "<FONT SIZE=+1>" + bodyCopy + "</FONT>" );
		document.write( "</TD></TR></TABLE>" );
	}
}

// -----------------------



// 20.  Swing/JMF dispatcher.

// There is an applet in the navigation panel whose only job is to see
// if the reader's system has Swing and the Java Media Framework
// already installed.  If so, great.  If not, then any applet that
// uses Swing or JMF must download a jar file.

function hasSwing()
{
  return ((typeof(top.moduleTitle)!="undefined") ||
	  (top.side_nav.swingjmfdispatcher.hasSwing()));
}

function hasJMF()
{
  return ((typeof(top.moduleTitle)!="undefined") ||
	  (top.side_nav.swingjmfdispatcher.hasJMF()));
}

// Use this in place of an <applet> tag if your applet uses Swing or
// JMF.
// Full example:
// appletBegin("links.applet.foo.FooApplet.class",
//             320, 240,
//             "../code/foo.jar,appletstuff.jar",
//             true, /* Uses Swing */
//             true, /* Uses JMF */
//             "../code/", true, "fred");
//
// Now, normally our applets do not use JMF, do not use a codebase,
// and are not scriptable.  Less than full example:
// appletBegin("links.applet.foo.FooApplet.class",
//             320, 240, "foo.jar,appletstuff.jar", true);

function appletBegin(code, w, h, jars, uses_swing, uses_jmf,
                     codebase, scriptable, name)
{
  var swingloc = module_root() + "shared/code/swing.jar";
  var jmfloc = module_root() + "shared/code/jmf.jar";
  var requires_swing_jar = ((uses_swing == true) && !hasSwing());
  var requires_jmf_jar = ((uses_jmf == true) && !hasJMF());

  var archive_string = jars;

  if (requires_swing_jar || requires_jmf_jar)
    {
      if (archive_string == null)
	{
	  archive_string = "";
	}
      else
	{
	  archive_string += ",";
	}

      if (requires_swing_jar && requires_jmf_jar)
	{
	  archive_string += swingloc + "," + jmfloc;
	}
      else if (requires_swing_jar)
	{
	  archive_string += swingloc;
	}
      else if (requires_jmf_jar)
	{
	  archive_string += jmfloc;
	}
    }

  document.writeln('<applet code="' + code + '"');
  if (scriptable == true) document.writeln(' mayscript="true"');
  if (codebase != null) document.writeln(' codebase="' + codebase + '"');
  if (name != null) document.writeln(' name="' + name + '"');
  if (archive_string != null)
    {
      document.writeln(' archive="' + archive_string + '"');
    }
  document.writeln(' width="' + w + '" height="' + h + '">');
}

// Similarly, this replaces the </applet> end-tag.
function appletEnd()
{
  document.writeln('</applet>');
}

// -----------------------------------------
