/*
[debugging alerts numbered # 1 through # 24]

Scheme for dual or quad jmols:

Formerly,
	standardButton(moleculeFilename, scriptFilename) [defined in buttons.js]
		stored button parameters in the buttons array.
	[standardButton() is now redefined in terms of setupButton(), for
	backwards compatibility.].

Now [defined below]
	setupButton(jmolCount,
		moleculeFilename1,	scriptFilename1, backgroundColor1[,
		mF2, sF2, bgC2[,
		mF3, sF3, bgC3[,
		mF4, sF4, bgC4]]])
			where jmolCount can be 1, 2, 3, or 4,
		will do the same, including e.g.
			buttons[buttonNumber][iMoleculeFilename][1] ... [4]
			and
			buttons[buttonNumber][iScript][1] ... [4]

Formerly, 
	makeButton(bnum) [defined in dvrblite.js] calls
		jmolButton( spt, buttons[bnum][iButtonLabel] );
		The spt is prepared from buttons[bnum][iScript], which along with
		the button label is stored by the call to standardButton

Now, it calls [defined below]
		makeJTATButton(bnum)

		Note that while there are 1-4 scripts, there is only one button and
		hence only one view description (button label). The button label could
		include a 2, 3, or 4 cell table with descriptions for multiple jmols.

		Each script will be processed by makeJTATScripts(bnum), storing
		the preprocessed scripts in nextScript[].

		Preprocessing is where a different molecule will be loaded prior to
		executing the rendering script, as needed.

Formerly, the button HTML included
	onClick=_jmolClick(scriptIndex, targetSuffix)
	which simply calls
		jmolScript(_jmol.scripts[scriptIndex], targetSuffix);

Now, the HTML returned by makeButton will have this replaced by
	multiJmolClick(bnum)
	where the jmol target suffix(es) are implicit.

Here is what multiJmolClick() does:

	if currentJmolCount != jmolCount,

		The jmols in the division are re-written.
		Upon loading, each jmol messages detectJmolReadyForJTAT,
			triggering sendNextScript().

		Scripts are not sent to their respective jmols until each is ready.

	if currentJmolCount == jmolCount,

		Scripts are sent to all jmols immediately.

MECHANISM FOR CONTENTS PAGE'S MOLECULAR VIEW(S):

The Contents page is also allowed to have 1-4 jmols.

contents.htm does two things:
 1. setupButton(jmolCount, moleculeFilename, scriptFilename[, ...])
 2. multiJmolClick(1) // there is only one "button" and it is number 1.

writeJTATButton() need not be called since there is no actual view button
in contents.

Initially, currentJmolCount is 0. This causes the correct number of Jmols
to be written, and their scripts to be sent.

*/
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// DEBUGGING CONTROLS
// See also some "author's" settings in config2.js

// Setting debugJTATSpt true opens the console and turns on debugscript
// for every View button pressed. It also suppresses the time consuming
// scripts such as drag2rotate.spt.
var debugJTATSpt = false;

// debugMemory reports "total" and "free" memory at the end of each
// view script. The report appears in a span to the right of the zoom slider.
// If there is no zoom slider, the report is not displayed.
// Should be false for released versions.
var debugMemory = false;

// noPreSptExit controls the prefixing of every view script with "exit;".
// "exit" is still used, regardless, by:
//    zoom slider
//    antialiasing toggle (smoother rotation/graphics)
//    centering
//    spin toggle
//    synch toggle
// This should be false for released versions.

var noPreSptExit = false;

// noZapping controls whether a zap is used before a "load xxxx.pdb",
// and before writing new jmols into the jmol division.
// Zapping was found to worsen the memory problem so noZapping should
// be true for released versions. doubleZap is ignored when noZapping
// is true.
var noZapping = true;
var doubleZap = true;

	var echoZap =
"set echo confirmZap 50% 50%;\n\
set echo confirmZap center;\n\
font echo 60 sans bold;\n\
color echo [xFFFF00];\n\
";
	if (doubleZap)
		echoZap += "echo ZAP ZAP!;\n";
	else
		echoZap += "echo ZAP!;\n";

	echoZap +=
"delay 0.5;\n";

// skipDelays should be false for released versions. It reduces time consumed
// in delay and moveTo commands.

var skipDelays = false;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// VARIABLES (non-debugging)
var jmolDotJsInitialized = false;
var jmolInitialized = new Array();
	jmolInitialized[1] = false;
	jmolInitialized[2] = false;
	jmolInitialized[3] = false;
	jmolInitialized[4] = false;
var detectJmolReady = "+-jmolReadyForJTAT-+";

var jmolsZapped = 0;
var waitingForZapping = false;
var zapCompletedMessage = "+-zap-completed-+";

var currentJmolCount = 0; // Zero: no Jmols written yet.
var currentButtonNumber = 0;
var nextButtonNumber = 0;
//var repeatViewCount = 0;

var viewNumberEchoPosition = "0% 0%";

var echoDrag2Rotate = "echo-drag2rotate";

// OBSOLETE
//var currentBackgroundColor = "";
var mainBackgroundColor = "000000"; // black

var currentMolecule = new Array();
var nextMolecule = new Array();
var nextScript = new Array();

// standard background colors
var whiteRGB = "FFFFFF";
var blackRGB = "000000";
var whiteBackground = whiteRGB;
var blackBackground = blackRGB;
var backgroundBlack = blackRGB; // these are just synonyms
var backgroundWhite = whiteRGB;

// initial spin parameters
var spinningOn = "spinningOn";
var spinningOff = "spinningOff";
var spinningSame = "spinningSame";
var spinningStart = "spinningStart";
var spinCount = 0;
var spinningStarted = new Array(false, false, false, false);

// reset button? (these are now legacy)
var resetButton = true;
var noResetButton = false;

// these are current (not legacy)
var resettingInitialView = false;

var syncOnCommand = "sync * on;";
// Bob also suggested
// sync * "set syncMouse";
// but I've used set syncMouse separately for each applet.
var syncOffCommand = "sync * off;";

var chapterTitle = '';
var chapterSubtitle = '';
var chapterIntro = '';
var chapterConclusion = '';

var contentsButtonGoesTo = '';
var provideResetButton = true;
var previousChapterButtonGoesTo = '';
var nextChapterButtonGoesTo = '';

var noIdea = "If you have no idea what we're talking about, don't worry. \n\
It is explained step by step in the instructions for authors.";

var windowTitle = "";

var centerModeUsed = false;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function setupButton(jmolCount,
	moleculeFilename1,	scriptFilename1, backgroundColor1, spinning1,
	moleculeFilename2,	scriptFilename2, backgroundColor2, spinning2,
	moleculeFilename3,	scriptFilename3, backgroundColor3, spinning3,
	moleculeFilename4,	scriptFilename4, backgroundColor4, spinning4)
{
	buttonNumber++;

	var badParameterMsg = "";

	// Check for valid jmolCount
	// isNaN returns false for empty string or space, true for undefined variable.

	if (isNaN(jmolCount))
			badParameterMsg = "first parameter, jmolCount=" + jmolCount +
			", not a number.";

	else if (jmolCount < 1 || jmolCount > 4)
			badParameterMsg = "jmolCount is " + jmolCount + ", valid range is 1-4.";

//	alert("dualquad.js #12a jmolCount=" + jmolCount);

	// Check for undefined or blank filenames.

	if (badParameterMsg.length == 0)
	{
		for (iparam = 1; iparam <= jmolCount; iparam++)
		{
			// PDB FILENAME
			param = eval("moleculeFilename" + iparam);
			
			if (typeof(param) == "undefined")
				badParameterMsg = "undefined moleculeFilename" + iparam;

			else if (param == "")
				badParameterMsg = "blank moleculeFilename" + iparam;

			// SCRIPT FILENAME
			param = eval("scriptFilename" + iparam);

			if (typeof(param) == "undefined")
				badParameterMsg = "undefined scriptFilename" + iparam;

			else if (param == "")
				badParameterMsg = "blank scriptFilename" + iparam;

			// BACKGROUND COLOR
			param = eval("backgroundColor" + iparam);

			if (typeof(param) == "undefined")
				badParameterMsg = "undefined backgroundColor" + iparam;

			else if (param == "")
				badParameterMsg = "blank backgroundColor" + iparam;

			// SPINNING
			param = eval("spinning" + iparam);

			if (typeof(param) == "undefined")
				badParameterMsg = "undefined spinning" + iparam;

			else if (param == "")
				badParameterMsg = "blank spinning" + iparam;

			else if (param != spinningOn && param != spinningOff &&
				param != spinningSame && param != spinningStart)		
					badParameterMsg = "invalid spinning" + iparam +
					"\n(must be one of spinningOn, spinningOff,\n" +
					"spinningSame or spinningStart).";
		}
	}

	if (badParameterMsg != "")
	{
		badParameterMsg = "Authoring error: setupButton() called with\n" +
			badParameterMsg +
			"\n (Button number " + buttonNumber + ")";
		alert(badParameterMsg);
		cancelButtonWriting = true;
		return;
	}

//	alert("dualquad.js #12b jmolCount=" + jmolCount +
//		"\n iJmolCount=" + iJmolCount);

	// All parameters checked. Proceed to set up array.	

	buttons[buttonNumber] = new Array();

// For mysterious reasons, this next statement has no effect!
	buttons[buttonNumber][iJmolCount] = jmolCount;
// It is repeated below, to effect. ??!!

	buttons[buttonNumber][iMoleculeFilename] = new Array();
	buttons[buttonNumber][iScript] = new Array();
	buttons[buttonNumber][iBackgroundColor] = new Array();
	buttons[buttonNumber][iSpinning] = new Array();

//	alert("dualquad.js #12c jmolCount=" + jmolCount +
//		"\n buttonNumber=" + buttonNumber +
//		"\n iJmolCount=" + iJmolCount +
//		"\n buttons[buttonNumber][iJmolCount]=" + buttons[buttonNumber][iJmolCount]);

	for (iparam = 1; iparam <= jmolCount; iparam++)
	{
		buttons[buttonNumber][iMoleculeFilename][iparam] =
			eval("moleculeFilename" + iparam);
		buttons[buttonNumber][iScript][iparam] =
			eval('scriptFilename' + iparam);
		buttons[buttonNumber][iBackgroundColor][iparam] =
			eval('backgroundColor' + iparam);
		buttons[buttonNumber][iSpinning][iparam] =
			eval('spinning' + iparam);
	}

	// Synchronize unless this is set to false after the setupButton() call.
	buttons[buttonNumber][iSync] = true;

	buttons[buttonNumber][iButtonType] = 'button'; // link, button, radio, or checkbox

	// reset, y, or yes to reset the display before running this script.
	// noreset or no to leave the current view alone.
	buttons[buttonNumber][iScriptMode] = 'display';

	buttons[buttonNumber][iButtonLabel] = 'View ' + buttonNumber;
	buttons[buttonNumber][iButtonPrefixText] = ''; // was '<br>', now div padding
	buttons[buttonNumber][iButtonText] =
		"AUTHOR: PUT VIEW DESCRIPTION HERE (in buttons[buttonNumber][iButtonText]).";
	buttons[buttonNumber][iScriptCheckboxUnchecked] = '';
	buttons[buttonNumber][iDetailsText] = '';
	buttons[buttonNumber][iDetailsTitle] = '';
	buttons[buttonNumber][iPreButtonText] = '';
	buttons[buttonNumber][iPostButtonText] = '';
	

//	buttons[buttonNumber][iJmolCount] = jmolCount;

//	alert("dualquad.js #11" +
//		"\n buttonNumber=" + buttonNumber +
//		"\n iJmolCount=" + iJmolCount +
//		"\n buttons[buttonNumber][iJmolCount]=" +
//			buttons[buttonNumber][iJmolCount] +
//		"\n buttons[buttonNumber][iMoleculeFilename][1]=" +
//			buttons[buttonNumber][iMoleculeFilename][1] +
//		"\n buttons[buttonNumber][iScript][1]=" +
//			buttons[buttonNumber][iScript][1] +
//		"\n buttons[buttonNumber][iBackgroundColor][1]=" +
//			buttons[buttonNumber][iBackgroundColor][1] +
//		"\n buttons[buttonNumber][iButtonText]=" +
//			buttons[buttonNumber][iButtonText]);

}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function makeJTATButton(bnum)
{
	if (cancelButtonWriting)
		return;

	// Scripts will be processed later, only upon button click!

	// Generate HTML for button

	var bttn = "";

	// ENCLOSE BUTTON IN DIVISION WITH VIEW-DEPENDENT COLORS
	var bgc = getButtonBackgroundColor(bnum);

	// NOTE: syntax for two classes is class="class1 class2"
	bttn += '<div class="button'; // double quote not ended in case another class
	if (bgc == whiteRGB)
		bttn += ' whitebackground';
	else
		bttn += ' blackbackground';
	bttn +=	'">\n'; // close class= double quote

	// ADD PREFIX TEXT (formerly "<br>")
	// iButtonPrefixText is no longer in use, and is always blank.
	// iPreButtonText is defined in chapter.js, and is blank by default.
	if (buttons[bnum][iButtonPrefixText] == '')
		buttons[bnum][iButtonPrefixText] = buttons[bnum][iPreButtonText];
	bttn += buttons[bnum][iButtonPrefixText] + "\n";
	if (buttons[bnum][iButtonPrefixText] != '')
		bttn += "<br>\n";


	// MAKE VIEW-BUTTON HERE
	bttn += '<input type="button" value="' +
		buttons[bnum][iButtonLabel] +
		'" onClick=\'multiJmolClick(' + bnum + ')\'>';
	// button end here

	bttn += makeAuthorInfo(bnum) + "\n";
	
	bttn += '&nbsp;&nbsp;' + buttons[bnum][iButtonText] + "\n";

	bttn += makeButtonDetailsControl(bnum);

	bttn += buttons[bnum][iPostButtonText];

	bttn += '<\/div>\n\n';

	return bttn;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// makeJTATScripts() preprocesses the scripts, storing the results
// in nextScript[]

function makeJTATScripts(bnum)
{
//	alert("dualquad.js #1: makeJTATScripts(" + bnum + ")");

	spinCount = 0;

	var useInitViewSpt = (bnum == 0);
	if (useInitViewSpt) bnum = 1;

	var jCount = buttons[bnum][iJmolCount];

	for (ijmol = 1; ijmol <= jCount; ijmol++)
	{
		// BEGIN spt CONSTRUCTION
		var spt = '';

		// INITIALIZE JMOL ONCE
		if (!jmolInitialized[ijmol])
		{
			spt += "script ../../_sptutil/initjmol.spt;\n";
			if (skipDelays)
				spt += "script ../../_sptutil/speedup.spt;\n";
			jmolInitialized[ijmol] = true;
		}

		// NEW MOLECULE?
		var newMol = buttons[bnum][iMoleculeFilename][ijmol];

		if (currentJmolCount != jCount)
		{
			// All jmols will be rewritten, so none have molecules yet.
			currentMolecule[1] = "";
			currentMolecule[2] = "";
			currentMolecule[3] = "";
			currentMolecule[4] = "";
		}

		if (currentMolecule[ijmol] != newMol)
		{

			// zap before loading new molecule
			if (currentMolecule[ijmol] != "") // don't zap if nothing loaded
			{
				if (!noZapping)
				{
					if (!doubleZap)
						spt += 'zap;\n' + echoZap;
					else
						spt += 'zap; zap;\n' + echoZap;
				}
			}

			spt += 'load "' + newMol + '";\n';
			currentMolecule[ijmol] = newMol;
		}

		var rst = ( buttons[bnum][iScriptMode] ).toLowerCase();
	
		if ( ( rst == 'reset' ) || ( rst == 'y' ) || ( rst == 'yes' ) )
		{
	
			spt += getResetSpt();
			spt += getReorientSpt();
			spt += getDisplaySpt();
		}

		if ( rst == 'display' )
		{
			// frieda added this extra condition to rst
			// it allows instantaneous change of structure, with no change in position
			// instead of structure flashing/repositioning before change appears
	
			spt += getResetSpt(); // simply calls vreset.spt
		}
	
		// BACKGROUND COLOR
		// Script may include "click and drag" echo delays, so background
		// must be before the delays.
		var bgc = buttons[bnum][iBackgroundColor][ijmol];
		spt += "background [x" + bgc + "];\n";

		// MAIN BUTTON SCRIPT	
		if (useInitViewSpt)
		{
			if (jCount == 1)
				spt += "script initial-view.spt;\n";
			else
				spt += "script initial-view" + ijmol + ".spt;\n";
		}
		else
			spt += buttons[bnum][iScript][ijmol] + ";\n";

		// ECHO NOW SHOWING ...
		if (!isContents)
		{
			var ecc = (bgc == blackRGB)? "CCCCCC" : "505050";
			// added by Frieda
			spt += "set echo viewNumber " + viewNumberEchoPosition + ";\n" +
				"font echo 16 sans bold;\n" +
				"color echo [x" + ecc + "];\n";

			if (jCount < 4)
				spt += "echo Now Showing ";
			else			
				spt += "echo ";

			spt += buttons[bnum][iButtonLabel]
			if (jCount > 1)
			{
				switch (ijmol)
				{
					case (1): spt += "A";
						break;
					case (2): spt += "B";
						break;
					case (3): spt += "C";
						break;
					case (4): spt += "D";
				}
			}
			spt += ";\n" ;
		}

		// INITIAL SPINNING
		var spindo = buttons[bnum][iSpinning][ijmol];
		if (spindo != spinningOff)
			spinCount++;

		if (!spinningStarted[ijmol]) // once at outset for each Jmol.
			// Put spinprep.spt at top of script, and only once per Jmol,
			// so that subsequent explicit spinFps spinAxes override it.
			spt = "script ../../_sptutil/spinprep.spt;\n" + spt;

		if (!noSpinning)
		{
			if (spindo == spinningOn)
				spt += "script ../../_sptutil/spin.spt;\n";
			else if (spindo == spinningOff)
				spt += "spin off;\n";
			else if (spindo == spinningStart && !spinningStarted[ijmol])
				spt += "script ../../_sptutil/spin.spt;\n";
			// else spinningSame, no addition to spt needed.
		}
		else // noSpinning
		{
			if (spindo == spinningOff)
				spt += "spin off;\n";
		}

		spinningStarted[ijmol] = true;

		// SYNCHRONIZATION
		// Set each Jmol to sync mouse but not scripts
		if (jCount > 1 && buttons[bnum][iSync])
			spt += "set syncScript false; set syncMouse true;";

		// Send sync command only to the last Jmol, only if none are spinning
		if (jCount > 1 && ijmol == jCount) 
		{
			if (buttons[bnum][iSync] && spinCount == 0) 
				spt += syncOnCommand;
			else
				spt += syncOffCommand;
		}

		// REPEAT VIEW (formerly "breathe")
		if (currentButtonNumber == bnum)
		{
			if (!resettingInitialView)
				spt += "script ../../_sptutil/flashviewnumber.spt;\n";

			resettingInitialView = false;

//			spt += "script ../../_sptutil/breathe.spt;\n";

//			if (ijmol == jCount)
//			{
//				++repeatViewCount;
//				if (repeatViewCount >= 2)
//				{
//					alert("View " + bnum + " is already showing.\n" +
//						"Below the molecule, at left, tells the view now showing.");
//					repeatViewCount = 0;
//				}
//			}

		}
//		else
//			repeatViewCount = 0;

		// DEBUGGING
		if (debugJTATSpt)	
			spt = "# dualquad.js #3;\n console;\n set debugscript;\n" + spt;

		if (debugMemory && ijmol == 1)
			spt += "javascript reportMemory();\n";

		// REPORT ZOOM TO SET SLIDER (Jmol 1 only)
		if (ijmol == 1)
			spt += 'message ' + viewZoomValueMsg + ';\nshow zoom;\n';

		// MARK BEGINNING AND END OF SCRIPT WITH COMMENTS
		var begincomm = '# === BEGIN SCRIPT FOR VIEW ' + bnum;
		if (jCount > 1)
			begincomm += ' Jmol ' + ijmol;
		begincomm += " === \n";

		spt = begincomm + spt;	

		spt += '# === END SCRIPT FOR VIEW ' + bnum + ' ===\n';

		// EXIT TO INTERRUPT ANY RUNNING SCRIPT
		// Eric added: When a button is clicked, any still-running script should
		// be unconditionally killed.
		// N.B. exit must precede the "begin view" comment!

		// SECOND
		if (debugScriptFiles)
			spt = "set historyLevel 1;\n" + spt;

		// FIRST
		if (!noPreSptExit)
			spt = "exit;\n" + spt;

		// END OF spt CONSTRUCTION
//		alert("dualquad.js #2: nextScript[" + ijmol + "] =\n" + spt);

		nextScript[ijmol] = spt;
	}
}
	
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function writeJmols(bnum)
{
	if (!jmolDotJsInitialized) // need initialization for page
	{
		// jmolInitialize() is REQUIRED!
		// It specifies the relative location of the applet.
		//
		// If you are serving this page from a Web server, call jmolInitialize 
		// with the relative path from this html file to the JmolApplet directory.
	
		// defaultjar (defined in Jmol.js) causes use of the single file
		// applet JmolApplet.jar, instead of the many-file applet JmolApplet0*.jar

		// jmolInitialize("../..", defaultjar); // once per page
		jmolInitialize("../.."); // once per page
	
		// jmolSetCallback() was added by Frieda.
		// It is required for Angel Herraez's spin state button below.
		// It must be betweeen initialize and applet calls.

		jmolSetCallback("messageCallback", "processMessageCallback"); // once per page

		// Eric added pickCallback for centering.
		jmolSetCallback("pickCallback", "processPickCallback"); // once per page

		jmolSetDocument(0); // this suppresses direct html writes by jmolButton()
		// see also makeControls()

		jmolDotJsInitialized = true;
	}
	
	var jCount = buttons[bnum][iJmolCount];

//	alert("dualquad.js #9 currentJmolCount=" + currentJmolCount +
//		" jCount=" + jCount);

	// If we already have the desired number of jmols, send scripts now.
	var spinCount = 0;
	if (currentJmolCount == jCount)
	{
		for (ijmol = 1; ijmol <= jCount; ijmol++)
		{
//			alert("dualquad.js #4:\n currentJmolCount = " + currentJmolCount +
//				"\n jmolCount = " + jCount +
//				"\n ijmol = " + ijmol +
//				"\n nextScript[ijmol] = \n" + nextScript[ijmol]);

			jmolScript(nextScript[ijmol], ("" + ijmol));

			if (buttons[bnum][iSpinning][ijmol] != spinningOff)
				spinCount++;
		}

		// CHECK/UNCHECK SYNC CHECKBOX
		if (jCount > 1)
		{
			if (spinCount == 0 && buttons[bnum][iSync])
				document.jmolForm.checksync.checked = true;
			else // may or may not be spinning ...
				document.jmolForm.checksync.checked = false;
		}

		return;
	}

	// WE NEED A DIFFERENT NUMBER OF JMOLS -- WRITE THEM NOW

	var jhtml = "";

	for (ijmol = 1; ijmol <= jCount; ijmol++)
	{
//		jmolReady[ijmol] = false;

		jhtml += makeOneJmol(jCount, ijmol);

		// vertical spacing
		if (jCount == 2 && ijmol == 1)
			jhtml += jmolBr() + jmolBr();

		else if (jCount == 3 && ijmol < 3)
			jhtml += jmolBr() + jmolBr();

		else if (jCount == 4 && ijmol == 2)
			jhtml += jmolBr() + jmolBr();

		// horizontal spacing
		if (jCount == 4 && (ijmol == 1 || ijmol == 3))
			jhtml += "&nbsp;";

	}

	jhtml += jmolBr(); // leaves some space below the applet for the Spin Toggle button.
	currentJmolCount = jCount; // needed for following ...
	jhtml += makeSpinToggleButton();

//	alert("dualquad.js #8: writeJmols(" + bnum + "):\n" + jhtml);

	document.getElementById("jmolAppletsDiv").innerHTML = jhtml;

}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
var jmolDefaultHeight = 94; // %
var jmolDefaultIEHeight = 91; // %
var jmolDefaultWidth = 100;

function makeOneJmol(jCount, ijmol)
{
	// GET APPLET SIZE
	// 1 applet
	var jheight = jmolDefaultHeight;
	if (_jmol.isIEWin)
		jheight = jmolDefaultIEHeight;
	var jwidth = jmolDefaultWidth;

	// 2 applets
	if (jCount == 2)
	{
		jheight /= 2; // was 47%
//		if (_jmol.isIEWin)
//			jheight = "45%";
//		jwidth = "100%";
	}

	// 3 applets
	else if (jCount == 3)
	{
		jheight /= 3; // was 31%
//		if (_jmol.isIEWin)
//			jheight = "30%";
//		jwidth = "100%";
	}

	// 4 applets
	else if (jCount == 4)
	{
		jheight /= 2;
//		if (_jmol.isIEWin)
//			jheight = "45%";
//		jwidth = "49%"; //@@ for border; was 50%; 49.5% same as 49%.
		jwidth /= 2;
		jwidth -= 1;
	}

	s_jheight = "" + jheight + "%";
	s_jwidth = "" + jwidth + "%";

	var jNameSuffix = "" + ijmol;

	//	initSpt = getStart();
	var readySpt = "message " + detectJmolReady + ";";

	var apphtml = "";

//	apphtml += "<div style='height:" + jheight + "; width:" + jwidth + "; " +
//		" border: 2px;'>";

	apphtml += jmolApplet( [s_jwidth, s_jheight], readySpt, jNameSuffix);

//	apphtml += jmolApplet( ["99%", "99%"], readySpt, jNameSuffix);

//	apphtml += "</div>";

// This had no effect
//  apphtml = jmolAppletAddParam(apphtml, "border", "10");

// This also had no effect
//	apphtml = apphtml.replace(/<object /, "<object border='10px' ");

//	alert("dualquad.js #6: applet html=\n" + apphtml);

	return apphtml;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function makeSpinToggleButton()
{
	// Show spin status for applet 1 only. Then toggle for all in
	// messageCallback().

	// messageCallback (messageProcess()) watches for the report from
	// show spin, and when it goes by, toggles spinning.

	// Single applet button was formerly:
	// jhtml += jmolButton("exit; show spin;", "toggle spinning");
	
	// <small><small> outside of form did not reduce size of blank line in IE.

//	var shtml = '<form name="jmolForm"><div id="spinToggleDiv" ';

	var shtml = '<div id="spinToggleDiv" ';

	if (!_jmol.isIEWin) // internet explorer
		shtml += 'style="margin-top: 4px;" ';

// NO EFFECT:
//	else
//		shtml += 'style="margin: 0px; padding: 0px;" ';

	// START FORM
	shtml += '>\n' +
		'<form name="jmolForm">\n';

	// ANTIALIASING
	shtml +=
'<span id="smootherSpan">\n\
<a href="javascript: toggleAntialiasing()">Smoother Rotation</a></span>\n\
</span> &nbsp; &nbsp; \n';

	// TOGGLE SPINNING BUTTON
	shtml += 
		'<input type="button" value="Toggle Spinning" \n' +
//		'onClick="jmolScript(\'exit; show spin;\', \'1\')">\n';

// In Jmol 12.0.8, exit via jmolScript turns spinning off, and the
// toggle code then always turns it back on (so it cannot be toggled
// off with the button, but can with "show spin" from the console.
// Prefixing show spin with ! kills the echo fade script without turning
// off spin. Bob proposed that in future, exit will not turn off spinning.

		'onClick="jmolScript(\'!show spin;\', \'1\')">\n';


	// WRITE SYNC CHECKBOX
	if (currentJmolCount > 1)
	{
		shtml +=
		
		' &nbsp; &nbsp; ' +

		// formerly: All molecules move together."
		'<span id="syncspan" title="Mouse rotation/zooming moves all molecules together.">' +		
		'<input type="checkbox" name="checksync" ';
		if (spinCount == 0)
			shtml += 'checked';
		shtml +=
		' onClick="setSync()">Synchronize</span>';
	}

	// WRITE CENTERING CHECKBOX
	shtml +=
		' &nbsp; &nbsp; ' +
		'<span id="centerspan" title="Check to center the atom you click.">' +		
		'<input type="checkbox" name="checkcenter" id="checkcenter"' +
		' onClick="setCenterMode(this.checked)">Center Clicked</span>';
		
	// END FORM AND DIVISION
	shtml += '</form></div>';

//	alert("dualquad.js #17: spinToggleButton html\n" + shtml);

	return shtml;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function sendNextScript(jmolSuffix)
{
	var ijmol = parseInt(jmolSuffix);

	if (nextScript[ijmol] == "")
		return;

//	alert("dualquad.js #5: sendNextScript(" + ijmol + ") sends\n" +
//		nextScript[ijmol]);

	jmolScript(nextScript[ijmol], ("" + jmolSuffix));
	nextScript[ijmol] = "";
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function multiJmolClick(bnum)
{
	if (checkZoomAutoslideValueTimerHandle != -1)
	{
//		alert("dualquad.js #21 clearTimeout(" + checkZoomAutoslideValueTimerHandle + ")");
		clearTimeout(checkZoomAutoslideValueTimerHandle);
		zoomAutoslideTimerStarted = false;
		debugZoomAutoslideTimerStarted();
	}
	lastMessagedZoomValue = 0;

	resetSlider();

	// bnum == 0 means use initial-view.spt, but only for jmolCount == 1!

	// preprocess scripts and store in nextScript[]
	makeJTATScripts(bnum);

	if (bnum == 0)
		bnum = 1; 

	nextButtonNumber = bnum;

	// processMessageCallback() will call multiJmolClick2() when all
	// jmols report completion of zapping.
	zapThenWriteJmols();
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function multiJmolClick2()
{
	// change number of Jmols (if needed) and trigger sending nextScripts
	// when they are ready.
	writeJmols(nextButtonNumber); 

	currentButtonNumber = nextButtonNumber;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function getButtonBackgroundColor(bnum)
{
	// If different Jmol have different background colors, use the majority color.
	// If there is a tie, use black.
	// Only black and white are supported.

//	alert("dualquad.js #14a: black=" + blackRGB + " white=" + whiteRGB);

	var whitec = 0;
	var blackc = 0;
	var retval = blackRGB;

	var jCount = buttons[bnum][iJmolCount];

	for (ijmol = 1; ijmol <= jCount; ijmol++)
	{
//		alert("dualquad.js #15:" +
//			"\n buttons[bnum][iBackgroundColor][ijmol]=" +
//			"\n buttons[" + bnum + "][" + iBackgroundColor + "][" + ijmol + "]=" +
//			buttons[bnum][iBackgroundColor][ijmol]);

		if (buttons[bnum][iBackgroundColor][ijmol] == whiteRGB)
			++whitec;
		else if (buttons[bnum][iBackgroundColor][ijmol] == blackRGB)
			++blackc;
	}

//	alert("dualquad.js #14b: black=" + blackRGB + " white=" + whiteRGB +
//		"\n blackc=" + blackc + " whitec=" + whitec);

	if (whitec > blackc) // black if whitec == blackc
		retval = whiteRGB;

//	alert("dualquad.js #10 getButtonBackgroundColor(bnum) returns\n" +
//		retval);
	
	return retval;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function showInitialView()
{
//	alert("dualquad.js #7 showInitialView() -> multiJmolClick(1)");

	multiJmolClick(0); // Contents page has only one virtual button, #1
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function resetInitialView()
{
	resettingInitialView = true;
	showInitialView();
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function setMainBackgroundColor()
{
	var whitec = 0;
	var blackc = 0;
	var bgc;

	var buttonCount = getButtonCount();
		
	for (var bnum = 1; bnum <= buttonCount; bnum++ )
	{
		var jCount = buttons[bnum][iJmolCount];
	
		for (ijmol = 1; ijmol <= jCount; ijmol++)
		{
			bgc = getButtonBackgroundColor(bnum);
			if (bgc == whiteRGB)
				++whitec;
			if (bgc == blackRGB)
				++blackc;
		}
	}

	if (whitec > blackc)
		mainBackgroundColor = whiteRGB;

	// else default blackRGB
	return ("blackc=" + blackc + " whitec=" + whitec);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function makeBodyTag(moreBody)
{
	var btt = '<body ';

	// altho ccounts is not used except in a debugging alert,
	// calling setMainBackgroundColor() sets mainBackgroundColor!
	var ccounts = setMainBackgroundColor();

	if (mainBackgroundColor == whiteRGB)
	{
		btt += ' class="whitebackground"';
	}
	// default body style is black background

	btt += moreBody + '>\n';

//	alert("dualquad.js #19: writeBodyTag() gets\n" +
//		ccounts + " and returns\n" + btt);

	return btt;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

function makeJmolDivTag()
{
	var btt = '<div id="jmolAppletsDiv" class="jmol'; // double quote not closed

	if (mainBackgroundColor == whiteRGB)
		btt += ' lightgraybackground';
	else
		btt += ' darkgraybackground';

	btt += '">\n';

//	alert("dualquad.js #20: makeJmolDiv() returns\n" + btt);

	return btt;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// For the methods used here, see
// http://developer.apple.com/internet/webcontent/dom2i.html

//function appendJmolDivTag()
//{
//	var jdivObj = document.createElement("DIV");
//	document.body.appendChild(jdivObj);
//}

function setJmolDivStyle()
{
	var jclass = "jmol";

	if (mainBackgroundColor == whiteRGB)
		jclass += ' lightgraybackground';
	else
		jclass += ' darkgraybackground';

	var jdiv = document.getElementById("jmolAppletsDiv");
	// I found this IE Win setAttribute trick in the June 9 posting at
	// http://inmyexperience.com/archives/000428.shtml (setAttribute)
	if (_jmol.isIEWin)
		jdiv.setAttribute("className", jclass);
	else
		jdiv.setAttribute("class", jclass);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function setSync()
{
	var synchek = document.jmolForm.checksync.checked;

	if (synchek)
		jmolScript(syncOnCommand, ("" + currentJmolCount));
	else
		jmolScript(syncOffCommand, ("" + currentJmolCount));
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function setCenterMode(ckmark)
{

/*
	// CRUDE ALERT METHOD
n	var clickCenter = true;

	// If this is not the first centering operation in this chapter,
	// offer to recenter the entire molecule.
	if (centerModeUsed && ckmark)
		 clickCenter = confirm("OK to CLICK on an atom to center it.\n\n" +
			"CANCEL to center the ENTIRE MOLECULE.");

	if (!clickCenter)
	{
		document.jmolForm.checkcenter.checked = false;

		// unconditionally center all Jmols
		jmolScript("zoomTo 1.5 (all) *1;", "all");
			
		// When >1 jmol, turn off the centering invitation in the non-clicked ones.
		// Harmless when there is 1 jmol.
		// No exit here to avoid killing centering process.
		jmolScript("set echo promptcenter off;", "all"); // redundant for clicked Jmol
	}

	else // clickCenter
	{
*/

		centerMode = ckmark;

		if (centerMode)
		{
			promptCenter();
		}

		else
		{
//			alert("dualquad.js #24 uncheck");
			jmolScript("set echo centerButton off;", "all");
		}

		centerModeUsed = true;

//	}

}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// initChapter() is the main initialization function. It is called onLoad
// by the contents and by each chapter.

function initChapter()
{
	if (!showZoomSlider && showSlabSlider)
	{
		alert("Author: Slab slider without zoom slider is not allowed.\n" +
		"Please reconfigure showZoomSlider and showSlabSlider in chapter.htm.");
		return;
	}

	setJmolDivStyle();

	showInitialView();

	initJmolControls();
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function setBodyStyle()
{
	// altho ccounts is not used except in a debugging alert (above),
	// calling setMainBackgroundColor() sets mainBackgroundColor!
	var ccounts = setMainBackgroundColor();

	// default body style is black background
	if (mainBackgroundColor == blackRGB)
		return;

	var bodski = document.getElementById("mainBodyTag");
	if (_jmol.isIEWin)
		bodski.setAttribute("className", "whitebackground");
	else
		bodski.setAttribute("class", "whitebackground");
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function writeControlPanel()
{
	writeChapterTitle();
	writeChapterIntro();
	writeResetInitialViewButton();
	writeViewButtons();
	writeChapterConclusion();
	writeBottomMatter();
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function writeChapterTitle()
{
	var chaptitle =
'<div class="chapterTitle">\n'

+ page_title +

'</div><div class="chapterSubtitle">\n'

+ page_subtitle +

'</div>';


//	if (disablePolling)
//		chaptitle = 

//"<p><font color='red'>\n\
//Polling is disabled. Zoom slider will not follow mouse.\n\
//To turn on polling, enter into the console\n\
//<br><tt><big>&nbsp; &nbsp; javascript disablePolling = false</big></tt>\n\
//<br>and then click any View button.\n\
//<hr noshade></font></p>\n\
//" +	chaptitle;

	document.writeln(chaptitle);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function writeChapterIntro()
{
	var chapintro = page_intro;

// It seems better to include <p></p> explicitly.
//'<p>\n' + page_intro + '\n</p>\n';

	if (chapintro == "" &&
		provideResetButton && showResetInitialViewButtonAtTop)
		chapintro = "<br>";

	document.writeln(chapintro);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function writeChapterConclusion()
{
	document.writeln(chapterConclusion);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function writeViewButtons()
{
	// controlsDiv is used only in case of the need to rewrite the controls.
	// see rewriteControls()
	// controlsDiv could be removed since rewriteControls() seems obsolete.

	var viewbuts =
'<div id="controlsDiv">\n'

+ makeControls() +

'\n</div>\n';

	document.writeln(viewbuts);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function supportLegacy()
{
	if (page_title == '')
		page_title = chapterTitle;
	if (page_subtitle == '')
		page_subtitle = chapterSubtitle;
	if (page_intro == '')
		page_intro = chapterIntro;

	if (nav_contents == '')
		nav_contents = contentsButtonGoesTo;

//	alert("dualquad.js #23: in supportLegacy(), provideResetButton = " +
//		provideResetButton);
	if (typeof(provideResetButton) != "undefined")
		nav_reset = provideResetButton;

	if (nav_previous == '')
		nav_previous = previousChapterButtonGoesTo;
	if (nav_next == '')
		nav_next = nextChapterButtonGoesTo;

}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function writeResetInitialViewButton()
{
	if (!showResetInitialViewButtonAtTop || !provideResetButton)
		return;

	var riv =
				'<center><input type="button" value="Replay/Reset Initial View" ' +
//				'title="View 1, resetting orientation and zoom" ' +
				'onclick="resetInitialView()" \/>' +
				'</center>';
	document.writeln(riv);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function zapThenWriteJmols()
{
	var nextJmolCount = buttons[nextButtonNumber][iJmolCount];

	// NO NEED TO ZAP WHEN:
	if (currentJmolCount == 0 // no Jmols written yet
			// or using existing PDB or loading new PDB into existing Jmols
		|| currentJmolCount == nextJmolCount
		|| noZapping)
	{
		jmolsZapped = buttons[nextButtonNumber][iJmolCount];
		multiJmolClick2();
		return;
	}

	// NEED TO ZAP: CHANGING NUMBER OF JMOLS

	jmolsZapped = 0;	

	// waitingForZapping tells processMessageCallback() to call
	// multiJmolClick2() when jmolsZapped equals currentJmolCount.

	waitingForZapping = true;

	for (ijmol = 1; ijmol <= currentJmolCount; ijmol++)
	{
		if (doubleZap)
			jmolScript("exit;\n" + "zap; zap;\n" + echoZap +
				"message " + zapCompletedMessage + ";",
				"" + ijmol);
		else
			jmolScript("exit;\n" + "zap;\n" + echoZap +
				"message " + zapCompletedMessage + ";",
				"" + ijmol);
	}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function reportMemory()
{
	var memstat = jmolEvaluate("_memory", "1");

//	var memtot = parseFloat(memstat.substring(0, memstat.indexOf("/")));
//	var memfree = parseFloat(memstat.substring(memstat.indexOf("/") + 1));

//	memtot = parseInt(memtot + 0.5);
//	memfree = parseInt(memfree + 0.5);

	// show raw report
	var memreport = "<a href='javascript: reportMemory()'>_memory</a>: " +
		memstat;

	// debugging ZZ link does zap zap.
	memreport += " <a href='javascript: zapzap()'>ZZ</a>";

	// show both raw and rounded reports
//	var memreport = "_m: " + memtot + "/" + memfree + " " + memstat;

	// show rounded integer report
//	var memreport = "_memory: " + memtot + "/" + memfree;

	// Sometimes, intermittantly, report failed to appear. So I tried
	// a delay which seemed to cure the problem.
	setTimeout('document.getElementById("memoryWatchSpan").innerHTML = "' +
		memreport + '"', 500);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function zapzap()
{
	jmolScript("zap; zap;", "1");
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// JmolControls are checkboxes etc. created with the jmolCheckbox()
// etc. functions in Jmol.js. writeJmolControls() is defined for
// each chapter that needs it, in jtat/tutorial_name/chapter_name/jcontrol.js.

function initJmolControls()
{
	if (typeof(writeJmolControls) != "undefined")
		writeJmolControls();
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
