// debugging alert numbers used through # 30
// PIPE.JS IS ADAPTED FROM PE.

var debugpipe = false;
var debug_pipe = false; // ADDED here since PE
var debugdetails = false;
var debugcolorize = false;
var setup_pipe_completed = false;

var pipe_primary_pdb = "";
//var pipe_primary_block = "";

var consurf_color_key = "";
var consurf_isd_color_key = "";
var consurf_isd_mode = "isd"; // see consurf/isd_mode.htm
var consurf_isd_mode_number = 1; // see consurf/isd_mode.htm
var consurf_chain_display_mode = "all"; // all spacefilled initially
var microheterogeneity = false;
//var show_isd = true; // isd = insufficient data assigned grade 0
var only_isd_data = false; // this is only for manually constructed test data

var serverName = "ConSurf";

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// ADDED during adaptation from PE

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// CONTENTS OF pipe.js:

// pipe.js contains top level functions needed to process the PiPE block
// in the header. Functions needed only within a PiPE control page
// are located in the folder pipe_sup.

// show_pipe_block() displays the pipe block then original header.

// setup_pipe()
//   calls parse_pipe() which divides block into typed segments (html, js, spt)
//    which uses getline()
//   runs js.init if any
//   sets default top control variables
//   changes the control page to pipe_cp1 (default or provided).

// expand_spts() TO BE CALLED AT RUN TIME!
// SO PUT_BUTTON CAN'T PUT THE ENTIRE SCRIPT LITERALLY

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
/* Management of background color:

The molecule is loaded first

The presence of the required _pipe.pdb (or _pipe.mmol) in the PDB filename
causes the FirstView display to be hidden.

 with a default FirstView and white background.
Then the PiPE block is parsed.

PiPE block may specify "black" or "white" for pipe_background_color,
which value is assigned in pipe.js set_pipe_controls()
to top.pec_bgcolor (defined in pex_cfg.js).


*/
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// Legacy top-level presentation variables are in pex_cfg.js

// The following are controlled by top.pipemode, which is a query
// parameter from the page that invokes the PiPE session:
//
// top.pec_ssfn = false; // hide scripts in button labels
// top.FontSize = 6; // Initial font size

// if any of these changed, change also in util_top.js reinitPiPE()
var pipe_background_color = "white"; // top.pec_bgcolor
var pipe_title_enlarged = true; // no control except possible js.init
var pipe_subtitle = "";

// Don't define default values for
// pipe_title: error message if not defined.
// pipe_start_spinning: default is "" = PE's preference

var PipeWin = ""; // Window opened to show the raw PiPE block
var htot;

var isSoftPiPE = false; // true if PiPE block generated from MolSlides
var softpipe = "";

var isPiPE = false; // true if a PiPE block exists in the header.
// In these variables, "block" refers to a sub-block or segment.
var pipe_blocks_total = 0;
var pipe_current_block = 0;
// FIRST BLOCK IS 1, NOT 0!
var pipe_block_type = new Array(); //html, js, spt
var pipe_block = new Array();

// Scripts.
var pipe_spts_total = 0;
var pipe_spt_i = new Array(); // Indices into main block array for spts.
var pipe_spt_name = new Array(); // Name of each script from first line.

// Details.
var pipe_details_total = 0;
var pipe_details_i = new Array(); // Indices into main block array for spts.
var pipe_details_name = new Array(); // Name of each script from first line.

// Colors.
var pipe_colors_total = 0;
var pipe_color_name = new Array();
var pipe_color_value = new Array();

// Used by getline().
var pbli = 0; // pipe block line index
var pbls = ""; // pipe block line string
var pblcnt = 0;

var ckboxmax = 6; // maximum number of chain checkboxes
var consurf_chain_checkbox = new Array();

// consurf_chain_checkbox[] boolean is used to recall which checkboxes
// are checked (e.g. when changing control panels).
// When the number of chains > ckboxmax, consurf_chain_checkbox[0]
// is used for a single "all identical chains" checkbox.

// used by ConSurf (top to survive exit/return to control panel)
// to tell whether a given grade is spacefilled (for toggle buttons).
var cssf_grade = new Array(10);

// for sequence insertions
var insertions_total = 0;
var insertion_at = new Array(); // sequence position
var insertion_csc_isd = new Array(); // consurf colors
var insertion_csc = new Array(); // consurf colors

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function show_pipe_block() // showheader showpdbheader
{
	var doctop, hsizes, docmid1, docmid2, docbot;
	var sptinfo = "";

	doctop = "<HTML>\n<HEAD>\n<TITLE>PiPE Block<\/TITLE>\n<\/HEAD>\n"
	doctop += "<BODY BGCOLOR=#FFFFFF>\n\n"

//	if (noheader)
//	{
//		hsizes = "<p><big><b>PDB file header has not been obtained.<\/b><\/big>";
//		docmid1 = docmid2 = "";
//		docbot = "\n<\/BODY>\n<\/HTML>";
//	}
//	else
//	{

		var hptot = pipeBlock.length;
		var hhtot = pdbHeader.length;
		htot = hptot + hhtot; // htot is external

		hsizes = "<big>The technical information below is for developers. ";
		hsizes += "You do not need this information to use FirstGlance in Jmol.<br>";
		hsizes += "The &quot;PiPE&quot; format used below was developed for ";
		hsizes += "&quot;Presentations in Protein Explorer&quot;, and adapted for ";
		hsizes += "display of ConSurf results in FirstGlance in Jmol.<br><br>";

		hsizes += "<p><big><b>" + htot + " bytes in the PDB file header: ";
		hsizes += hptot + " (" + phtot(hptot) + "%) in PiPE block + ";
		hsizes += hhtot + " (" + phtot(hhtot) + "%) in original PDB file header.";
		hsizes += "<\/b><\/big>";

		hsizes += "<p><hr noshade>";

		hsizes += "<big><b>PiPE Block ";
		hsizes += "<\/b><\/big><pre><xmp>";

		docmid1 = "<\/xmp><\/pre>";

		// COLORS
		sptinfo += "<p><hr noshade>The above PiPE block contains <b>";
		if (pipe_colors_total == 0)
			sptinfo += "no named colors<\/b>.";
		else
		{
			sptinfo += pipe_colors_total + " named color(s):<\/b><ol>";
			for (i=1; i <= pipe_colors_total; i++)
			{
				sptinfo += "<li><b>";
				sptinfo += pipe_color_name[i];
				sptinfo += " </b>has value<b> ";
				sptinfo += pipe_color_value[i] + "<\/b>";
			}
			sptinfo += "<\/ol>";
		}

		// SCRIPTS
		sptinfo += "<p>The above PiPE block contains <b>";
		if (pipe_spts_total == 0)
			sptinfo += "no named scripts.<\/b>";
		else
		{
			sptinfo += pipe_spts_total + " named script(s):<\/b><ol>";
			for (i=1; i <= pipe_spts_total; i++)
			{
				sptinfo += "<li>";
				sptinfo += "<\/b>PiPE block segment number<b> " + pipe_spt_i[i];
				sptinfo += " <\/b>is a script block with a script named<b> ";
				sptinfo += pipe_spt_name[i] + "<\/b>";
			}
			sptinfo += "<\/ol>";
		}

		// DETAILS
		sptinfo += "<p>The above PiPE block contains <b>";
		if (pipe_details_total == 0)
			sptinfo += "no popup details.<\/b>";
		else
		{
			sptinfo += pipe_details_total + " popup detail(s):<\/b><ol>";
			for (i=1; i <= pipe_details_total; i++)
			{
				sptinfo += "<li>";
				sptinfo += "<\/b>PiPE block segment number<b> " + pipe_details_i[i];
				sptinfo += " </b>contains popup details named<b> ";
				sptinfo += pipe_details_name[i] + "<\/b>";
			}
			sptinfo += "<\/ol>";
		}

		docmid2 = "<p><hr noshade>";
		docmid3 = "<b><big>Original Header of the Primary PDB File:<\/big><\/b><pre><xmp>";

//	var docbot = "\n<\/XMP><\/PRE>[Header PiPE block ends on previous line]\n<\/BODY>\n<\/HTML>";

		docbot3 = "\n<\/XMP><\/PRE>";
		docbot2 = "\n<hr noshade><\/BODY>\n<\/HTML>";

//	} //if noheader, else ...

	if (PipeWin != "")
	{
		if (!PipeWin.closed)
			PipeWin.close();
	}
	PipeWin=open("","PipeWin",
		"titlebar=1,menubar=1,status=1,resizable=1,scrollbars=1," +
		"width=" + (top.outerWidth - 100) +
		",height=" + (top.outerHeight - 100) + ",screenX=40,screenY=30");

	with (PipeWin.document)
	{
		open();
		write(doctop);
		write(hsizes);
//		if (!noheader)
//		{
			write(pipeBlock);
			write(docmid1);
			write(sptinfo);
//		}
//		else
//			write(docmid1);

		write(docmid2);
		write(docmid3);
//		if (!noheader)
		write(pdbHeader);
		write(docbot3)

		write(docbot2);
		close();
	}
	PipeWin.focus();
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function show_invalid_pipe_block()
{
	var rawhead = pdbheader_pipe[top.activei()];
	rawhead = rawhead.replace(/>/g, "&gt;");
	rawhead = rawhead.replace(/</g, "&lt;");

	if (PipeWin != "")
	{
		if (!PipeWin.closed)
			PipeWin.close();
	}
	PipeWin=open("","PipeWin",
		"titlebar=1,menubar=1,status=1,resizable=1,scrollbars=1," +
		"width=" + (top.outerWidth - 100) +
		",height=" + (top.outerHeight - 100) + ",screenX=40,screenY=30");

	with (PipeWin.document)
	{
		open();
		writeln("<big><b>Invalid Pipe Block:</b></big><br><br>");
		writeln("<pre>");
		write(rawhead);
		writeln("<\/pre>");
		close();
	}
	PipeWin.focus();
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function phtot(x) // % of header total
{
	if (x == 0)
		return (0);
	return(parseInt(0.5 + ((100 * x)/htot)));
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// setup_pipe() is called from hcquery.js (see #14) after a non-empty
// pipe block has been separated from the rest of the PDB file header.

function setup_pipe()
{
	if (top.debug_pipe)
		alert("pipe.js #23: entering setup_pipe()");

	if (pipeBlock.indexOf("consurf_run_number") != -1)
	{
		top.consurfMode = true;
		useConsurfColors = true;		
		// serverName default is "ConSurf"
		maxGrades = 9;
	}

	else if (pipeBlock.indexOf("pepsurf_run_number") != -1)
	{
		top.pepsurfMode = true;
		useConsurfColors = true;
		serverName = "Pepitope";
	}

	else if (pipeBlock.indexOf("selecton_run_number") != -1)
	{
		top.selectonMode = true;
		useConsurfColors = true;
		serverName = "Selecton";
		maxGrades = 7;
	}

	else if (pipeBlock.indexOf("epitopia_run_number") != -1)
	{
		top.epitopiaMode = true;
		consurf_isd_mode = "";
		useConsurfColors = true;
		serverName = "Epitopia";
		maxGrades = 5;
	}

	if (consurfMode || pepsurfMode || selectonMode || epitopiaMode)
		telAvivMode = true;

	if (pipeBlock.indexOf("select_grade4") == -1)
		top.only_isd_data = true;

	if (top.debug_pipe)
		alert("pipe.js #26:" +
			"\n consurfMode=" + top.consurfMode +
			"\n only_isd_data=" + top.only_isd_data);

//	set_pipemode(); // audience, individual, development

	if (!setup_pipe2())
	{
//		alert("pipe.js #21 parse_pipe() fails");
		if (confirm("Failed PiPE window will close.\n" +
			"(Cancel to keep window open for testing.)"))
			top.window.close();
	}
	
	if (pepsurfMode)
		setup_pepsurf();
	else if (selectonMode)
		setup_selecton();
	else if (epitopiaMode)
		setup_epitopia();
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// setup_pipe2() is separated from setup_pipe() so it can be postponed
// until ask_pipemode gets a response.

function setup_pipe2()
{
	if (!parse_pipe()) // Divides PiPE block into typed segments ("blocks").
		return false;

	if (pipe_blocks_total == 0)
	{
		isPiPE = false;
		setup_pipe_completed = true;
		return true;
	}
	else
	{
		isPiPE = true;
	
		// run pre-control panel javascript if any; set up array of colors
		for (i = 1; i <= pipe_blocks_total; i++)
		{
			if (pipe_block_type[i] == "js.init")
			{
//				if (confirm("pipe.js #4: init?\n" + pipe_block[i]))

				eval(pipe_block[i]);
			}
//			if (pipe_block_type[i] == "spt")
//			{
//				// Expand scripts within scripts at RUN TIME!
//			}

			if (pipe_block_type[i] == "color")
			{
				pipe_colors_total++;
				var coline = clean_and_trim_ends(pipe_block[i]);
				var isp = coline.indexOf(" ");

				// if space following RGB value, discard remainder of line
				var isp2 = coline.indexOf(" ", isp + 1);
				if (isp2 != -1)
					coline = coline.substring(0, isp2);

				if (isp == -1)
				{
					alert("PiPE ERROR: PiPE processing halted.\n" +
						"\"!color \" line:\n" +
						pipe_block[i] +
						"lacks a space separating the color name from\n" +
						"the color value.");
					pipe_blocks_total = 0;
					isPiPE = false;
					setup_pipe_completed = true;
					return false;
				}
				if (coline.substring(0, 6) != "color_")
				{
					alert("PiPE ERROR: PiPE processing halted.\n" +
						"Color name in \"!color \" line:\n" +
						pipe_block[i] +
						"(first word) must begin with \"color_\".");
					pipe_blocks_total = 0;
					isPiPE = false;
					setup_pipe_completed = true;
					return false;
				}

				pipe_color_name[pipe_colors_total] = coline.substring(0, isp);
				var pcv = coline.substring(isp + 1);
				if (!top.only(pcv.toUpperCase(), "ABCDEF0123456789") ||
					pcv.length != 6)
				{
					alert("PiPE ERROR: PiPE processing halted.\n" +
						"Color RGB value in \"!color \" line:\n" +
						pipe_block[i] +
						"is not 6 characters in length, or contains\n" +
						"non-hexadecimal characters (not 0123456789ABCDEF).");
					pipe_blocks_total = 0;
					isPiPE = false;
					setup_pipe_completed = true;
					return false;
				}
				pipe_color_value[pipe_colors_total] = pcv;

//				alert("pipe.js #14: pipe_block[" + i + "]:\n" +
//					escape(pipe_block[i]) + "\n" +
//					"pipe_colors_total " + pipe_colors_total + "\n" +
//					"pipe_color_name " + escape(pipe_color_name[pipe_colors_total]) + "\n" +
//					"pipe_color_value " + escape(pipe_color_value[pipe_colors_total]));
			}
		}

		// now that we've evaluated the initialization javascript
		set_pipe_controls();

		if (consurfMode) // freq checks; NOT selectonMode
		{
			if (!check_cs_freq_total(true)) // isd true
				return false;

			// only_isd_data
			if (typeof(consurf_grade_freqs) != "undefined") // for manual test PDB's
				if (!check_cs_freq_total(false)) // isd false
					return false;
		}

		if (telAvivMode)
			init_chain_checkboxes();

	}

	if (!epitopiaMode) 
	{
		isd_color = get_colorvalue("color_grade0");
		if (isd_color == "FFFF96") // ConSurf default
			isd_color = "C0C000"; // darker shade better for dots
//	alert("pipe.js #29 isd_color = " + isd_color);
	}

	setup_pipe_completed = true;
	return true;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// parse_pipe() divides the master PiPE block into sub-blocks by type,
// namely html, js, spt.
// It initializes pipe_block[i], pipe_block_type[i], pipe_blocks_total

// FIRST BLOCK IS 1, NOT 0!

function parse_pipe()
{
	pipe_blocks_total = 0;

	var i, w1, pbtype, lminustype;
	var gottype = false;
	var oldtype = "";

	// Unlike other block types, consecutive script blocks need to be
	// separated into separate blocks.
	var newsptblock;
	var newdetailsblock;
	// Every !color line is a new color block, but we have to detect
	// invalid continuation lines.
	var newcolorblock;

	var raw_pipe_block = pipeBlock;

	// strip "\r" out of PiPE block (leaving "\n").
//	if (top.isWin)
//		raw_pipe_block = raw_pipe_block.replace(/\r/g, "");
//	else // top.isMac: uses \r only  (see hcquery.js clean_header_formfeeds())
//		raw_pipe_block = raw_pipe_block.replace(/\r/g, "\n");

	// divide pipe block into sub-blocks by type html, js, spt.
	pbli = 0;
	while (getline(raw_pipe_block, "pbli", "pbls"))
	{
		newsptblock = false;
		newdetailsblock = false;
		newcolorblock = false;

		// getline() puts the line into pbls including the \n
		pblcnt++;

		// Ignore lines containing only "!" (spacers)
		// These are typically at the ends of blocks and will be regenerated
		// by editpipe.js.
		if (pbls == "!\n" || pbls == "! \n")
		{
//			alert("pipe.js #6:\nskipped line\n" + escape(pbls));
			continue;
		}

		// save comments (space NOT required after !!)
		else if (pbls.substring(0, 2) == "!!")
		{
			// temporarily discard
			continue;

			// later, to generate the header from arrays (if I really need to do that)
			// prefix each LINE (within a segment) that is prefixed with a comment
			// with ^C. Store the comment segments in an array, and just stick the
			// next one in whenever you hit a ^C in a line. This means the header
			// must be generated line by line, not segment by segment.

			// if the type before this line was not !!, it is the first comment line
			// if (pbtype != "!!")
			// to be continued ...
		}

		else
		{
			// get first word
			i = pbls.indexOf(" ");
			if (i == -1)
				i = pbls.length; // keep trailing newline
			w1 = pbls.substring(0, i); // does not include space
	//		alert("pipe.js #2: w1 = \"" + w1 + "\"");
			// trim w1 off front of line
			lminustype = pbls.substring(i + 1);
	//		alert("pipe.js #13: lminustype:\n" + escape(lminustype));
	
			// GET TYPE
			// If "! " type is inherited, or if undefined, html.
			if (w1 == "!") // if first word (w1) is "!" there was a space after !
			{
				if (!gottype)
					pbtype = "html";
				// else keep type unchanged
			}
			// No space after ! so type is specified.
			else // if (w1.charAt(1) != " ")
			{
				pbtype = w1.substring(1); // trim leading "!".
				// lminustype is ... in "!xxx ..."
	//			alert("pipe.js #11: lminustype:\n" + escape(lminustype));
	
				// If "!html\n" instead of "!html \n" or "!html more\n",
				// then pbtype ends in \n.
				if (pbtype.charAt(pbtype.length - 1) == "\n")
				{
					pbtype = pbtype.substring(0, pbtype.length -1);
	//			alert("pipe.js #7, \\n trimmed from pbtype:\n" + escape(pbtype));
				}
	
				// ------ SCRIPTS ------
				// Record script name for lines beginning !spt #name=...
				// Must be here (before recording block) so it is not recorded
				// on continuation lines "! ".
				if (pbtype == "spt")
				{
					newsptblock = true;
	
					// spt MUST have format "!spt #name=script_name[ ...]\n"
					// where script_name is one word and ends in a space or newline,
					// "#" is required,
					// and ... following script_name is ignored as a comment.
	
					// trim leading (and trailing) white space
					if (debugpipe)
						alert("pipe.js #8: lminustype:\n" + escape(lminustype));
					var lminustypetrimmed = top.clean_and_trim_ends(lminustype);
					if (debugpipe)
						alert("pipe.js #9: lminustypetrimmed:\n" + escape(lminustypetrimmed));
	
					// Insist on "#name=" next
					if (lminustypetrimmed.substring(0,6) != "#name=")
					{
						alert("PiPE ERROR: PiPE processing halted.\n" +
						"Script name does not begin with \"#name=\"\n" +
						"(embedded spaces disallowed) in this line:\n" + pbls);
						pipe_blocks_total = 0; // clears PiPE; forces isPiPE to false later.
						return(false);
					}
	
					// Quotes not permitted
					if (lminustypetrimmed.substring(0,7) == "#name=\""
						|| lminustypetrimmed.substring(0,7) == "#name='")
					{
						alert("PiPE ERROR: PiPE processing halted.\n" +
						"Please remove the quotes from the script name\n" +
						"in this line:\n" + pbls);
						pipe_blocks_total = 0; // clears PiPE; forces isPiPE to false later.
						return false;
					}
	
					// Reject SR's put_script_name_here
					if (lminustypetrimmed.indexOf("put_script_name_here") != -1)
					{
						alert("PiPE ERROR: PiPE processing halted.\n" +
						"Please replace \"put_script_name_here\" with a name\n" +
						"of your own, such as view1, view2, etc. in this line:\n" + pbls);
						pipe_blocks_total = 0; // clears PiPE; forces isPiPE to false later.
						return false;
					}
					
					// Get script name and store it with index to block
					var sptname = lminustypetrimmed.substring(6);
					// truncate name at first space (line was cleaned above)
					var ispace = sptname.indexOf(" ");
					if (ispace != -1)
						sptname = sptname.substring(0, ispace);
					if (debugpipe)
						alert("pipe.js #10: final script name is\n" + escape(sptname));
					pipe_spts_total++;
					// pipe_current_block hasn't been incremented yet.
					pipe_spt_i[pipe_spts_total] = pipe_current_block + 1;
					pipe_spt_name[pipe_spts_total] = sptname;
					
					if (debugpipe)
						alert("pipe.js #12: !spt number " + pipe_spts_total + "\n" +
							"at block number " + pipe_spt_i[pipe_spts_total] + "\n" +
							"for sptname " + pipe_spt_name[pipe_spts_total]);
				}
	
				// ------ DETAILS ------
				// Record details name for lines beginning !details #name=...
				// Must be here (before recording block) so it is not recorded
				// on continuation lines "! ".
				if (pbtype == "details")
				{
					newdetailsblock = true;

					// MUST have format "!details #name=details_name[ ...]\n"
					// where details_name is one word and ends in a space or newline,
					// "#" and "details_..." are required.
					// ... following script_name is ignored as a comment.
	
					// trim leading (and trailing) white space
					if (debugpipe)
						alert("pipe.js #16: lminustype:\n" + escape(lminustype));
					var lminustypetrimmed = top.clean_and_trim_ends(lminustype);
					if (debugpipe)
						alert("pipe.js #17: lminustypetrimmed:\n" + escape(lminustypetrimmed));
	
					// Insist on "#name=" next
					if (lminustypetrimmed.substring(0,6) != "#name=")
					{
						alert("PiPE ERROR: PiPE processing halted.\n" +
						"Details name does not begin with \"#name=\"\n" +
						"(embedded spaces disallowed) in this line:\n" + pbls);
						pipe_blocks_total = 0; // clears PiPE; forces isPiPE to false later.
						return false;
					}
	
					// Quotes not permitted
					if (lminustypetrimmed.substring(0,7) == "#name=\""
						|| lminustypetrimmed.substring(0,7) == "#name='")
					{
						alert("PiPE ERROR: PiPE processing halted.\n" +
						"Please remove the quotes from the details name\n" +
						"in this line:\n" + pbls);
						pipe_blocks_total = 0; // clears PiPE; forces isPiPE to false later.
						return false;
					}
	
					// Get details name and store it with index to block
					var dname = lminustypetrimmed.substring(6);
					// truncate name at first space (line was cleaned above)
					var ispace = dname.indexOf(" ");
					if (ispace != -1)
						dname = dname.substring(0, ispace);
					if (debugpipe)
						alert("pipe.js #18: final details name is\n" + escape(dname));

					if (dname.substring(0, 8) != "details_")
					{
						alert("PiPE ERROR: PiPE processing halted.\n" +
							"Details name in \"!details \" line:\n" +
							pbls +
							"must begin with \"details_\".");
						pipe_blocks_total = 0;
						return false;
					}

					pipe_details_total++;
					// pipe_current_block hasn't been incremented yet.
					pipe_details_i[pipe_details_total] = pipe_current_block + 1;
					pipe_details_name[pipe_details_total] = dname;
					
					if (debugpipe || debugdetails)
						alert("pipe.js #19: !details number " + pipe_spts_total + "\n" +
							"at block number " + pipe_details_i[pipe_details_total] + "\n" +
							"for dname " + pipe_details_name[pipe_details_total]);

					// details text is undefined at this point.

				}

				// ------ COLORS ------
				if (pbtype == "color")
					newcolorblock = true;
			}
	
			// Is type valid?
			//alert("pipe.js #15: pbtype = " + pbtype);

			if (pbtype != "html" && pbtype != "js" && pbtype != "spt"
				&& pbtype != "js.init" && pbtype != "color" && pbtype != "details")
			{
				alert("PiPE ERROR: PiPE processing halted.\n" +
					"Invalid PiPE type at line " + pblcnt + " of PDB header:\n" +
					pbls + "Valid types are  !!   !html   !js.init   !js   !color   !details   !spt\n" +
					"Each must be separated from any additional text on the line\n" +
					"by a space. No spaces can precede the first \"!\".");

				if (confirm("Show Invalid PiPE Block?"))
					show_invalid_pipe_block();
				pbtype = "invalid";
				pipe_blocks_total = 0; // clears PiPE; forces isPiPE to false later.
				return false;
			}

		}

		// NOW WE HAVE A VALID TYPE in pbtype.
//		if (!confirm("pipe.js #20: " +
//			"\n pbtype = " + pbtype +
//			"\n oldtype = " + oldtype +
//			"\n precommtype = " + precommtype +
//			"\n" + pbls))
//				return;

		if (oldtype != pbtype || newsptblock || newdetailsblock || newcolorblock)
		{
			pipe_blocks_total++;
			pipe_current_block++; // first block is 1
			pipe_block_type[pipe_current_block] = pbtype;
			if (pbtype == "details")
				pipe_block[pipe_current_block] = "";
			else
				pipe_block[pipe_current_block] = lminustype;
			oldtype = pbtype;
			gottype = true;
		}
		else // concatenate current line onto current block
		{
			if (pbtype == "color" && pbls.substring(0, 7) != "!color ")
			{
				alert("PiPE ERROR: PiPE processing halted.\n" +
					"Invalid line:\n" +
						pbls +
					"Every line specifying a color name and value\n" +
					"must begin \"!color \". Continuation lines are\n" +
					"not allowed.");
				pipe_blocks_total = 0; // clears PiPE; forces isPiPE to false later.
				return false;
			}
			pipe_block[pipe_current_block] += lminustype;

			if (debugdetails && pbtype == "details")
				alert("pipe.js #28: pipe block " + pipe_current_block +
				" =\n" + pipe_block[pipe_current_block]);
		}
//		alert("Type is " + pbtype + " at\n" + pbls);

	}

	return true;

//	var msg = "pipe.js #3: PiPE blocks total " + pipe_blocks_total + ":\n";
//	for (i = 1; i <= pipe_blocks_total; i++)
//	{
//		msg += "Block " + i + " of type " + pipe_block_type[i] + ":\n";
//		msg += pipe_block[i];
//	}
//	alert(msg);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// getline() returns the next line, keeping track of where it is with
// the external variable named iname.
// Returns true until there are no more lines, then false.
// Because the index is external and a parameter, in principle getline()
// could be used concurrently on two different tasks.

// value of variable named iname must be set to 0 before the first call.

function getline(ss, iname, lname)
{
	// get external index, where to look for next line
	var i, ll, retval;
	eval("i = " + iname + ";");
	if (i == -1)
		return false;

	// get newline if any -- could use top.end_character (not top.line_end)
	var ii = ss.indexOf("\n", i);

	// no newline
	if (ii == -1)
	{
		// there may be a final line without a trailing newline.
		ll = ss.substring(i); // entire remainder
		if (ll.length > 0)
		{
			retval = true;
		}
		else retval = false;
	}

	// newline found
	else
	{
		ll = ss.substring(i, ii + 1); // keeps newline at the end
		ii++;
		retval = true;
	}

	if (retval)
	{
		eval(iname + " = ii;"); // update external index; can be -1
		eval(lname + " = ll;"); // update external text line
	}

//	alert("pipe.js #1: getline() returning " + retval + "\n" +
//		"index = " + ii + "\n" +
//		"line = \"" + ll + "\"");
	return retval;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// colorize_html() supports this syntax
// <font @color_name>...</font>...

function colorize_html(hraw)
{
	var sptname;
	var i; // Index of first block is 1 not 0.
	var ispt,	jspt, isptname, icut, inew, snew;

//alert("hraw " + hraw);

	// while <font @color_name> remains to be replaced with the color value ...
	// (<font color_name> maintained for backwards compatibility w/ PE <=2.721)
	while(
		(ispt = hraw.indexOf("<font color_")) != -1 ||
		(jspt = hraw.indexOf("<font @color_")) != -1)
	{
		if (ispt == -1)
		{
			ispt = jspt;	
			hraw = hraw.substring(0, jspt + 6) + hraw.substring(jspt + 7);
//			alert("pipe.js #24 jspt = " + jspt +
//			"\n @ removed from @color_*\n" + hraw);
		}

		// set beginning of sptname
		isptname = ispt + 6;
//alert("isptname " + isptname);

		// find end of sptname
		icut = hraw.indexOf(">", isptname);

		if (icut == -1 ||
			(hraw.indexOf(" ", isptname) < icut) ||
			(hraw.indexOf("\n", isptname) < icut))
		{
			alert("PiPE ERROR in PiPE !html:\n" +
				"\">\" absent immediately following color_name:\n" +
				hraw.substring(ispt) +
				"Named HTML color(s) will fail to be applied\n" +
				"from this color name forward.");
			return(hraw);
		}

		// extract the script name
		sptname = hraw.substring(isptname, icut);
//alert("sptname " + escape(sptname));

		// locate the color_name
		var inew = -1;
		for (i = 1; i <= top.pipe_colors_total; i++)
		{
			if (sptname == top.pipe_color_name[i])
			{
				inew = i;
				break;
			}
		}
		if (inew == -1)
		{
			alert("PiPE ERROR: there is no !color whose name\n" +
				"matches \"<font " + sptname + ">\".\n" +
				"Named HTML color(s) will fail to be applied\n" +
				"from this color name forward.");
			return(hraw);
		}
		else
		{
			snew = top.pipe_color_value[inew];
			if (debugcolorize)
				alert("pipe_cp.js #4 pre colorize_html():\n" + hraw);
			hraw = hraw.substring(0, ispt) + "<font color=\"#" + snew + "\"" +
				hraw.substring(icut);
			if (debugcolorize)
				alert("pipe_cp.js #5 post colorize_html():\n" + hraw);
		}
	}
	return hraw;
}	
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function set_pipe_controls()
{
	top.pec_bgcolor = top.pipe_background_color;

	var title_ok = false;
	if (typeof(top.pipe_title) != "undefined")
	{
		if (top.clean_and_trim_ends(top.pipe_title) != "")
			title_ok = true;
	}
	if (!title_ok)
	{
		alert("PiPE Error: Title not specified in\n" +
			"!js.init pipe_title = \"Title of Presentation\"");
	}

	if (typeof(top.pipe_start_spinning) != "undefined")
	{
		if (top.pipe_start_spinning)
			top.pec_start_spinning = "t"; // override PE preference
		else
			top.pec_start_spinning = "f"; // override PE preference
	}
	// else leave pec_start_spinning "", default to PE preference
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function force_background_color(newbg)
{
	if (newbg != "white" && newbg != "black")
	{
		alert("PiPE Programmer's ERROR: background color\n" +
		"in script must be white or black but is:\n" +
		"javascript top.force_background_color(\"" + newbg + "\")");

		return;
	}

	if (top.bg_color == newbg)
		return;

	// toggle will set new top.bg_color!
	top.fr_control.fr_ur.toggle_bkg_when_ready();
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function clear_cssf_grades(booval)
{
//	alert("pipe.js #30 clear_cssf_grades(" + booval + ")");
	for (var i = 0; i < 10; i++)
	{
		cssf_grade[i] = booval;

		if (epitopiaMode)
			consurf_chain_checkbox[i] = booval;
	}
}

clear_cssf_grades(true);

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function check_cs_freq_total(isd)
{
	// check sum of frequencies in consurf_grade_freqs[]
	var sum = 0;
	var sisd = "";
	for (var i = 0; i < 10; i++)
	{
		if (isd)
		{
			sum += consurf_grade_freqs_isd[i];
			sisd = "_isd";
		}
		else
		{
			sum += consurf_grade_freqs[i];
//		sum += 1; // to test failure
		}
	}

	// even if sum != consurf_atom_seq_length, see below
	if (isd)
		consurf_grade_freqs_isd[10] = sum;
	else
		consurf_grade_freqs[10] = sum;

	if (sum != consurf_atom_seq_length)
	{
		if (consurf_atom_seq_length > sum)
		{
			alert(
"FirstGlance in Jmol assumes that this PDB file\n" +
"contains sequence microheterogeneity because\n" +
"Sum of consurf_grade_freqs" + sisd + "\n" +
"Grades 0-9 (" + sum + ") is less than length of\n" +
"sequence from ATOM records (" + consurf_atom_seq_length + ").\n" +
"Help will appear automatically");

			microheterogeneity = true;
			consurf_atom_seq_length = sum;
			window.open("../../my_molecules/pipes/bundled/consurf_pdb/index.htm#1cbn");
		}
		else
		{
			if (confirm("ConSurf Internal Error: Sum of consurf_grade_freqs" + sisd + "\n" +
			"Grades 0-9 (" + sum + ") is greater than length of\n" +
			"sequence from ATOM records (" + consurf_atom_seq_length + ").\n" +
			"Click OK to close session. (Cancel for debugging.)"))

				top.window.close();

			return false;
		}
	}

	// check total number of color grades in seq3d_grades + insertions
	var grades = new Array();
	var icsc = new Array();
	if (isd)
	{
		grades = seq3d_grades_isd;
		icsc = insertion_csc_isd;
//		sum = seq3d_grades_isd.length;
	}
	else
	{
		grades = seq3d_grades;
		icsc = insertion_csc;
//		sum = seq3d_grades.length;
	}

	// deduct gaps (periods)
//	for (i = 0; i < sum; i++)
//		if (grades.charAt(i) == ".")
//			sum--;
	sum = grades.replace(/\./g, "").length;

	// add insertions
	var insum = 0;
	for (i = 0; i < insertions_total; i++)
		insum += icsc[i].length - 1;

	if ((sum + insum) != consurf_atom_seq_length)
	{
		alert("ConSurf Internal Error: Total color grades\n" +
			"in seq3d_grades" + sisd + " less gaps (" + sum + ") plus insertions\n" +
			"(+" + insum + "=" + (sum + insum) + ") does not equal length of sequence\n" +
			" from ATOM records (" + consurf_atom_seq_length + ").\n" +
			"(see check_cs_freq_total() in protexpl/pipe.js)");
		return false;
	}

	return(true);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function init_chain_checkboxes()
{
	if (epitopiaMode)
	{
		for (var i = 0; i < ckboxmax; i++)
			consurf_chain_checkbox[i] = true;
		return;
	}

	var lpclist = "";
	if (consurfMode)
		lpclist = consurf_identical_chains.toUpperCase();
	else if (selectonMode)
		lpclist = selecton_identical_chains.toUpperCase();
	else if (typeof(pepsurf_identical_chains) != "undefined")
		lpclist = pepsurf_identical_chains.toUpperCase();
//	else if (typeof(epitopia_processed_chains) != "undefined")
//		lpclist = epitopia_processed_chains.toUpperCase();

	if (lpclist == "")
		return;

	var pctot = lpclist.length;
	var c1;
	if (consurfMode || selectonMode)
		c1 = consurf_chain.toUpperCase();
	else if (pepsurfMode)
		c1 = pepsurf_chains.toUpperCase();

	for (var i = 0; i < ckboxmax; i++)
		consurf_chain_checkbox[i] = false;

	if (pctot > ckboxmax)
		return;

	if (pctot >= 2 && pctot <= ckboxmax) // individual chain checkboxes
	{
		if (consurfMode || selectonMode)
		{
			for (var i = 0; i < ckboxmax; i++)
			{
				if (c1 == lpclist.charAt(i))
					consurf_chain_checkbox[i] = true;
			}
		}
		else // pepsurfMode
		{
			for (var i = 0; i < ckboxmax; i++)
			{
				if (c1.indexOf(lpclist.charAt(i)) != -1)
					consurf_chain_checkbox[i] = true;
			}
		}
	}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
function insertion(at, icsc, csc)
{
	// first in 0th member of arrays
	insertion_at[insertions_total] = at;
	insertion_csc_isd[insertions_total] = icsc;
	insertion_csc[insertions_total] = csc;
	insertions_total++;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//function show_download_info()
//{
//	window.open("download.htm");
//}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

