// Debugging alerts thru # 15
// UTILITIES DEFINED ELSEWHERE
// firstWord(s) top.js
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function goSecure() // replace HTTP with HTTPS
{
//alert(window.location.pathname); // after :// so for file:/// starts with /
if (window.location.protocol == "http:")
window.location.protocol = "https:";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function dw(x)
{
document.writeln(x);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function jmolReady()
{
reportElapsed("jmolReady() called (util.js)");
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function jmolAtomCount(atomexp) // model 1, config 1
{
var toJmol = "{(" + atomexp + ")";
if (modelCount > 1)
toJmol += " and model=" + modelToSelect;
// altLocCount is number of atoms in altLocs
toJmol += " and conformation1}.count";
//if (alertJmolAtomCount)
// alert("util.js #15 jmolAtomCount: " + toJmol);
return jmolEvaluate(toJmol); // typeof returned value is number
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function jmolAtomCountAllConfigs(atomexp)
{
var toJmol = "{(" + atomexp + ")";
if (modelCount > 1)
toJmol += " and model=" + modelToSelect;
toJmol += "}.count";
return jmolEvaluate(toJmol);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function jmolAtomCountAllModels(atomexp)
{
var toJmol = "{(" + atomexp + ")";
// altLocCount is number of atoms in altLocs
toJmol += " and conformation1}.count";
return jmolEvaluate(toJmol);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function repeat(char, times)
{
var dups = "";
for (ird = 1; ird <= times; ird++) dups += char;
return dups;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function match(name, subname)
{
// alert("util.js #2 match:\n" + name + "\n" + subname);
if (name.toLowerCase().indexOf(subname.toLowerCase()) == -1)
return false;
return true;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// example: setCharAt("012345", "3", "-") returns "01-345".
function setCharAt(str,index,chr)
{
if(index > str.length-1) return str;
return str.substr(0,index) + chr + str.substr(index+1);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// example: cutFromTo("one little pig", "lit", " pig") returns "little".
function cutFromTo(str, start, endbefore)
{
var ist = str.indexOf(start);
if (ist == -1)
return "";
var ieb = str.indexOf(endbefore, ist);
return str.substring(ist, ieb);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function capitalize(str) // ABC or abc -> Abc
{
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function cap1only(str) // "EM" -> "EM" not "Em"
{
return str.charAt(0).toUpperCase() + str.substring(1);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function countChar(str, chr)
{
var ctc = 0;
ichr = 0;
while ( (ichr = str.indexOf(chr, ichr)) != -1)
{
ichr++;
ctc++;
}
return (ctc);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// TEST CASES
// PROTEIN
// 1d66 protein, dna
// 3zen protein, unk, missing (with long chain >3,000!)
// 4fzv A: MSE, B: UNK, missing in both including missing UNK
// 4dgw C: MSE and UNK and missing
// 3pq1 MSE and UNK and VARIABLE missing in seqID chains.
// 3ou0 has 2 peptide chains of 100% UNK!
// 1w5u has 8 nonstd residues/chain (gramicidin).
// 4ev2 has nonstd TYQ
// NUCLEIC
// 4b5r rna
// 1evv rna with 14 nonstd, no missing.
// 1i2x, 104d, 2l7d dna/rna hybrid
var stdAA = ["ALA", "ARG", "ASN", "ASP", "CYS", "GLN", "GLU",
"GLY", "HIS", "ILE", "LEU", "LYS", "MET", "PHE", "PRO", "SER",
"THR", "TRP", "TYR", "VAL", "ASX", "GLX"];
var stdDNA = ["DA", "DC", "DG", "DT", "DU", "DI"];
var stdRNA = ["A", "C", "G", "T", "U", "I"];
var MSE = new Array;
MSE[0] = "MSE";
var UNK = new Array;
UNK[0] = "UNK";
var N = new Array;
N[0] = "N";
var mseLink = '';
var cseLink = '';
var nsrlinked = 'non-standard \
residue(s)';
var nonStandardResidueCount = 0;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// determineChains() in moltab.js constructs res from SEQRES, thereby excluding ligands+
// with the same chain name.
function getChainType(res) // res is an array of residue names from SEQRES.
{
var clen = res.length;
// Get counts of matching word types.
var p = countMatchingWords(res, stdAA);
var d = countMatchingWords(res, stdDNA);
var r = countMatchingWords(res, stdRNA);
// D-amino acids are handled later in chainTypeFromName() in moltab.js (see #76).
//alert("util.js #4 clen p d r:\n" + clen + " " + p + " " + d + " " + r);
// If all residues are of one type, we're done.
if (p == clen)
return ("Protein");
if (d == clen)
return ("DNA");
if (r == clen)
return ("RNA");
// THERE ARE NONSTANDARD RESIDUES OR UNK OR N
var unk = countMatchingWords(res, UNK);
var ncount = countMatchingWords(res, N);
var mse = countMatchingWords(res, MSE);
//alert("util.js #4a clen unk ncount mse:\n" + clen + " " + unk + " " + ncount + " " + mse);
var notmse = clen - p - mse - unk;
nonStandardResidueCount += (notmse + mse);
// alert("util.js #1 clen=" + clen + " p=" + p + " mse=" + mse +
// " notmse=" + notmse + " unk=" + unk);
// May 2024: All 1,637 entries containing UNK contain protein.
// No Nucleic only nor Oligosacc only contain UNK.
// In the PDB Format, UNK is for unknown amino acids (not nucleotides, which are N).
if (p > 0) // In Feb 2025, no entries with only 1 chain have protein and DNA or RNA polymer.
{
var pmsg = "Protein";
if (mse > 0)
{
pmsg += " including " + mse + " " + mseLink +
"selenomethionines (MSE)";
if (notmse > 0)
pmsg += " and " + notmse + " other " + nsrlinked;
}
else if (notmse > 0)
pmsg += " including " + notmse + " " + nsrlinked + " (no " +
mseLink + "selenomethonine [MSE])";
if (unk > 0)
{
if (mse > 0 || notmse > 0)
pmsg += " and ";
else
pmsg += " including ";
pmsg += unk + " unknown residue(s) [UNK]";
}
return pmsg + ".";
}
// NUCLEIC WITH NONSTD, OR UNKNOWN [N] = NUCLEOTIDE N. >>> See n.txt <<<
// May 2024: PDB has 96 entries with N.
// 6 are in "Nucleic only". N13,14 in chain A in 7e9e as phosphoribose. cf 6e1w.
// 8 are in "Protein only" as ligands: "Any 5'-monophosphate nucleotide".
// In 1g9s,1phw,3dzi(AU),8u0z, modeled as phosphoribose.
// 82 are in "Protein/NA".
// IF NONSTD = 0 WE WILL SAY "with 0 non-standard residue(s)" BECAUSE
// THEN THERE ARE Ns.
nonstd = clen - (d + r + ncount);
var nmsg = "";
if (ncount > 0) // [N]
nmsg = " and " + ncount + " unknown nucleic residues [N]";
// HYBRID DNA+RNA
if (d > 0 && r > 0)
{
var hmsg = "DNA (" + d + ") and RNA (" + r + ")";
if (nonstd > 0)
hmsg += " including " + nonstd + " " + nsrlinked;
return hmsg + nmsg + ".";
}
// DNA
if (d > 0 && r == 0)
return ("DNA including " + nonstd + " " + nsrlinked + nmsg + ".");
// RNA
if (r > 0 && d == 0)
return ("RNA including " + nonstd + " " + nsrlinked + nmsg + ".");
if (unk == clen) // Inserted "Protein" in FG4.3
return ("Protein: 100% unknown amino acids [UNK].");
else if (ncount == clen)
return ("Nucleic: 100% unknown nucleic residues [N].");
else
return ("Unrecognized (not protein, DNA nor RNA)" +
nmsg.replace(/ and /, " with ") + ".");
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// both words and tomatch are arrays.
function countMatchingWords(words, tomatch)
{
var mw = 0;
for (var iw = 0; iw < words.length; iw++)
{
for (var im = 0; im < tomatch.length; im++)
{
if (words[iw] == tomatch[im])
{
mw++;
break;
}
}
}
return (mw);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function singleSpace(str)
{
while (str.indexOf(" ") != -1)
{
str = str.replace(/ /g, " ");
}
return (str);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function arrayIndexOf(list, key)
{
var ai = -1;
for (qi = 0; qi < list.length; qi++)
{
if (list[qi].toLowerCase() == key.toLowerCase())
{
ai = qi;
break;
}
}
// alert("util.js #3 arrayIndexOf " + key + " is " + ai +
// ":\n" + list[ai] + "\n" + list);
return ai;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// 3 FUNX FROM PE
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function clean_word_string(to_do)
{
// replace newlines with spaces
var re = /\n/g;
to_do = to_do.replace(re, " ");
var re = /\r/g;
to_do = to_do.replace(re, " ");
// replace tabs with spaces
var re = /\t/g;
to_do = to_do.replace(re, " ");
re = / /g; // two spaces
// collapse any multiple spaces to single spaces
while (to_do.indexOf(" ") != -1)
to_do = to_do.replace(re, " ");
// trim off leading spaces
while (to_do.charAt(0) == " ")
to_do = to_do.substring(1);
return to_do;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function clean_and_trim_ends(s)
{
// \t,\n to spaces, spaces singularized, leading spaces trimmed
s = clean_word_string(s);
s = trim_ends(s);
return s;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function trim_ends(s, which) // which is undefined (both), "L" (leading), "T" (trailing)
{
var c1;
if (typeof(which) == "undefined") which = "B";
which = which.toUpperCase();
// trim leading end
if (which == "B" || which == "L")
{
while (true)
{
if (s.length == 0)
break;
c1 = s.charAt(0);
if (c1 == " " || c1 == "\n" || c1 == "\t" || c1 == "\r")
s = s.substring(1);
else
break;
}
}
if (which == "B" || which == "T")
{
// trim trailing end
while (true)
{
if (s.length == 0)
break;
c1 = s.charAt(s.length - 1);
if (c1 == " " || c1 == "\n" || c1 == "\t" || c1 == "\r")
s = s.substring(0, s.length - 1);
else
break;
}
}
return s;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// example: trimWord1(" wx yz ") returns "yz ".
function trimWord1(wrds)
{
wrds = trimWord1only(wrds); // leaves spaces after 1st word
// remove leading spaces
while (wrds.charAt(0) == " ")
wrds = wrds.substring(1);
return wrds;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function trimWord1only(wrds)
{
// remove leading spaces
while (wrds.charAt(0) == " ")
wrds = wrds.substring(1);
// remove leading nonspaces
var isp = wrds.indexOf(" ");
if (isp != -1)
wrds = wrds.substring(isp);
else // no space
wrds = "";
return wrds;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function getWordArray(s1) // returns array of non-blank words
{
var st = s1.trim();
return (st.split(/\s+/)); // one or more whitespace characters
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function isValidPDBId(pid)
{
if (pid.length != 4)
return(false);
pid = pid.toUpperCase();
// First character must be 1-9
if (pid.charAt(0) < '1' || pid.charAt(0) > '9')
return(false);
// Last three characters must be 1-9 or A-Z
if (!pdbIdAlphaNum(pid, 1))
return(false);
if (!pdbIdAlphaNum(pid, 2))
return(false);
if (!pdbIdAlphaNum(pid, 3))
return(false);
// all tests passed
return(true);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function pdbIdAlphaNum(pid, pos)
{
var x = pid.charAt(pos);
if ((x >= '0' && x <= '9') ||
(x >= 'A' && x <= 'Z'))
return true;
else return false;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function roundTo3Digits(val)
{
if (val >= 100.0) return parseInt(val);
else if (val >= 10.0) return roundToTenth(val);
else if (val >= 1.0) return roundToHundredth(val);
else return roundToThousandth(val);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function roundToTenthPct(val) // e.g. 0.1236 -> 12.4%
{
return (parseInt((1000. * val) + 0.5))/10.;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function roundToTenth(val) {return roundPctToTenth(val);}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function roundPctToTenth(val) // e.g. 1.25 -> 1.3
{
return (parseInt((10. * val) + 0.5))/10.;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function roundToHundredth(val) // e.g. 1.236 -> 1.24
{
return (parseInt((100. * val) + 0.5))/100.;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function roundToThousandth(val) // e.g. 1.2346 -> 1.235
{
return (parseInt((1000. * val) + 0.5))/1000.;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Instead, use val.toFixed(N) where N is number of digits after decimal point.
function padTo3DecimalPlaces(val)
{
var newlen = 5;
if (val >= 10.) newlen = 6;
// assumes largest value is 99.xxx
tval = "" + val;
if (!match(tval, ".")) tval += ".";
while(tval.length < newlen) tval += "0";
return tval;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Right-aligns a number, for max 99,999.
// If val is 1, returns 5 nbsp. If val is 10,000, returns blank.
function nbspBefore(val)
{
nal = " "; // for comma
if (val < 10000) nal += " ";
if (val < 1000) nal += " ";
if (val < 100) nal += " ";
if (val < 10) nal += " ";
nal += numberWithCommas(val);
return nal;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function toggleFFAdvice()
{
toggleDivVisibility("firefoxAdvice");
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// from
// http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
// handles up to 999,000,000,000 one trillion 10^12 - 1.
function numberWithCommas(x) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
while (parts[0].charAt(0) == ",") parts[0] = parts[0].substring(1);
return parts.join(".");
/* untested code
var vvs = parts[0];
var vv3 = [];
vv3[0] = vvs.substr(1,3);
vv3[1] = vvs.substr(4,3);
vv3[2] = vvs.substr(7,3);
vv3[3] = vvs.substr(10,3);
var newvvs = vv3.toString(); // commas are automatic but trailing ,,, for blank items.
while (newvvs.substr(-1) == ",") newvvs = newvvs.substr(0, newvvs.length - 1);
parts[0] = newvvs;
*/
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function isInteger(val)
{
var regex = /^[0-9]+$/;
if (val.match(regex)) return true;
return false;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function firstUseFind()
{
if (confirm(FirstUseFind_txt))
window.open("where.htm#f");
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* REDUNDANT, DEFINED ABOVE!
function capitalize(w)
{
var cw = w.charAt(0).toUpperCase();
cw += w.substr(1).toLowerCase();
return cw;
}
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function getZoom()
{
var zz = jmolScriptEcho("show zoom");
// zz is "zoom NNN.NNN"
zz = zz.substr(5);
//alert(zz);
return (parseFloat(zz));
// another possibility
//alert(jmolGetPropertyAsString("orientationInfo");
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// When you click the red T:
function eltimReport()
{
var rpttrailer = 'Clock and Gap/Script numbers have been reset to zero.
' +
'You may initiate actions, then click "T" again.';
var nothingdone = 'No actions have been initiated since the previous report.';
//alert(eltimGapTot + " " + typeof(eltimGapTot));
// GENERATE totrpt: Total elapsed times report for top of both reports:
var tottot = (eltimGapTot + eltimSptTot).toFixed(1);
// round totals
eltimGapTot = eltimGapTot.toFixed(1); // number to rounded string, 1 decimal place.
eltimSptTot = eltimSptTot.toFixed(1);
var totrpt = 'Total elapsed times:
' +
// Gap time
' ' + eltimGapTot + ' sec for Gaps between scripts ' +
'(execution of FirstGlance javascript not within JSmol).
';
if (eltimReported) totrpt +=
' Long gaps may be waits for manual actions.
';
// Script execution time.
totrpt +=
' ' + eltimSptTot + ' sec for execution of All Scripts by JSmol. ';
if (!eltimReported)
totrpt += 'Script 2 includes time to get PDB file via network.';
else
totrpt += 'Movie scripts (Ends, Crosslinks) add playing time'
totrpt += '
';
// Total time (1st report only)
if (!eltimReported) totrpt +=
' ' + tottot + ' sec Total.
' +
// Total number of scripts
eltimSptNumber + ' total scripts executed';
if (eltimReported) totrpt += ' since previous timing report.';
else totrpt += '.
';
// totrpt COMPLETED
// sort summary
eltimSummary = eltimSummary.split("\n"); // to array of lines
eltimSummary.sort();
eltimSummary.reverse();
eltimSummary = eltimSummary.join("\n"); // to lines in string
// OPEN 2 WINDOWS
eltimWindow = window.open(""); // open first so it is to the right of summary list.
eltimSummaryWindow = window.open("");
// ALL SCRIPTS REPORT
with (eltimWindow.document)
{
writeln('');
writeln('');
writeln('
');
writeln('All Scripts');
writeln('');
writeln('');
writeln('');
writeln('FirstGlance ELTIM:
' +
'Script Execution Times + Gaps Between Scripts
');
writeln(totrpt);
writeln('See the browser tab Timing Summary which lists longest times first.
');
writeln(''); // required for line breaks
if (eltimList.length)
{
for (iel = 1; iel < eltimList.length; iel++)
{
//if (typeof(eltimList[iel]) == "undefined") alert("undef at iel=" + iel);
// last element is undefined, so use < not <=.
writeln(eltimList[iel]);
}
}
else
writeln(nothingdone);
writeln('\n');
writeln(rpttrailer);
writeln('\n');
writeln('\n');
}
// TIMING SUMMARY REPORT
with (eltimSummaryWindow.document)
{
writeln('');
writeln('');
writeln('');
writeln('Timing Summary');
writeln('');
writeln('');
writeln('');
writeln('FirstGlance ELTIM:
' +
'SUMMARY of Script Execution Times + Gaps Between Scripts:
');
writeln(totrpt);
writeln('See the browser tab All Scripts for details.
');
writeln(''); // required for line breaks
if (eltimSummary.length)
writeln(eltimSummary);
else
writeln(nothingdone);
writeln('');
writeln(rpttrailer);
writeln('');
writeln('');
}
//alert("util.js #7: eltimList.length " + eltimList.length + ".\n" +
// "0th is undefined. 18th:\n" + eltimList[18]); // last element is undefined.
eltimList = [];
eltimGapTot = 0.0;
eltimSptTot = 0.0;
eltimSummary = "";
eltimGapNumber = 1;
eltimSptNumber = 1; // eltimReport() called by clicking the red T.
eltimReported = true;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function eltimPrepSpt(spt) // also logs spt in eltimList[].
{
// if no prior midGAPs, blank.
if (typeof(eltimList[eltimSptNumber]) == "undefined") eltimList[eltimSptNumber] = "";
eltimLogGap(); // adds to eltimList[].
// eltim additions to spt
// record actual execution start time
spt =
'javascript eltimSptStart[' + eltimSptNumber + '] = ' + elapsedSeconds(eltimStart) + ';\n' +
'javascript eltimExecutingSptNumber = ' + eltimSptNumber + ';\n\n' +
spt;
// record time when script execution is completed.
spt += '\njavascript eltimSptDone(' + eltimSptNumber + '); ' +
'# Brackets script with eltim commands and logs the above script.\n';
// THIS ALERT CAUSES WEIRD JMOL FAILURE "a is undefined"!
//alert("util.js #5: eltimPrepSpt(spt) with eltim additions:\n" + spt);
// Fixup script for log.
logspt = spt + '------ END OF SCRIPT ' + eltimSptNumber + '. ------\n';
// fix "<"
logspt = logspt.replace(/javascript reportElapsed');
logspt += midSptComments;
// log logspt
//if (eltimSptNumber == 1)
// eltimList[eltimSptNumber] += repeat("-", 80) + '\n';
eltimList[eltimSptNumber] += 'Script ' + eltimSptNumber + ':\n' + logspt;
eltimSptNumber++;
return spt; // for execution
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function eltimLogGap() // records gap before this script.
{
// Get previous gap time.
var gaptime = elapsedSeconds(eltimGapStart);
var clocktime = elapsedSeconds(eltimStart);
// New gap begins when a script is ready to send to JSmol.
eltimGapStart = new Date(); // reset gap start to now.
// If this is the gap waiting for the next manual action, don't record it.
/*
if (!eltimList.length && eltimReported)
{
// But announce 1st script.
eltimList += 'Script ' + eltimSptNumber + ' going to JSmol:\n' + spt;
return;
}
*/
// list end of gap followed by 'horizontal rule'.
eltimGapTot += parseFloat(gaptime);
if (typeof(eltimList[eltimSptNumber]) == "undefined") eltimList[eltimSptNumber] = "";
eltimList[eltimSptNumber] += '\n' + gaptime + ' sec for Gap ' + eltimGapNumber + '. ' +
'Clock time at end of gap: ' + clocktime + ' sec. ' +
'Total gap time until now: ' + eltimGapTot.toFixed(1) + ' sec.\n' +
repeat("-", 80) + '\n\n';
if (gaptime < 10.0)
eltimSummary += '0'; // prepend "0" to e.g. 0.1 -> 00.1 for sorting lines.
eltimSummary += gaptime + ' sec for Gap ' + eltimGapNumber + '.\n';
eltimGapNumber++;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// eltimSptDone() is called by a javascript command appended to every
// script processed by unchangedScriptToJmol().
function eltimSptDone(snum) // records script execution time.
{
var endtime = elapsedSeconds(eltimStart);
var spttime = endtime - eltimSptStart[snum];
//alert("util.js #6: eltimSptDone() spttime=" + spttime);
eltimList[snum] += '' + spttime.toFixed(1) +
' sec for Execution of Script ' + snum + ' ' +
'(Clock ' + eltimSptStart[snum] + ' to ' + endtime + ' sec.)\n';
eltimList[snum] += repeat("-", 80) + '\n';
if (spttime < 10.0)
eltimSummary += '0';
eltimSummary += spttime.toFixed(1) + ' sec for Execution of Script ' + snum + '.\n';
eltimSptTot += parseFloat(spttime);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// handles both MidSPT and MidGAP.
// MidSPT messages are generated by makeReportElapsedSpt(locn) which automatically
// prepends "MidSPT: " (midsptmark) to the location message.
function eltimMid(msg)
{
// mid spt
if (msg.indexOf(midsptmark) == 0) // 0: msg begins with "MidSPT: ", put in automatically.
{
var atspt = eltimExecutingSptNumber;
var attime = elapsedSeconds(eltimStart);
var spttime = attime - eltimSptStart[atspt];
eltimList[atspt] += '' +
spttime.toFixed(1) + ' sec in Spt ' + atspt + ' at "' + msg + '"\n';
}
else // mid gap
{
var atspt = eltimSptNumber; // may be ahead of executing number
// if (typeof eltimList[atspt] == "undefined") alert("UNDEF");
// else alert("DEF");
if (typeof(eltimList[atspt]) == "undefined") eltimList[atspt] = "";
// if (typeof eltimList[atspt] == "undefined") alert("UNDEF");
// else alert("DEF");
var attime = elapsedSeconds(eltimGapStart); // separate gap clock
var grpt = attime + ' sec at MidGAP ' + eltimGapNumber +
': "' + msg + '"';
//alert(eltimList[atspt]);
eltimList[atspt] += '' + grpt + '\n';
//alert('util.js #8: gap added for Spt' + atspt + ':\n' + eltimList[atspt]);
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// reportElapsed is used in "gaps" (javascript not within JSmol) AND also
// within scripts "javascript reportElapsed(msg)" where "MidSpt: " is prepended to msg.
function reportElapsed(msg)
{
if (eltimActive) return(eltimMid(msg));
// OBSOLETE code follows:
if (!reportElapsedTimes) return;
// Initialize reporting window
if (typeof(reportElapsedWindow) == "undefined")
{
reportElapsedWindow = window.open("");
with (reportElapsedWindow.document)
{
writeln('');
writeln('');
writeln('');
writeln('Elapsed Times');
writeln('');
writeln('');
writeln('');
writeln('Reports of Elapsed Time');
writeln(
'
' +
'In Firefox (local mode), ' +
'viewing this as a tab during loading of 1d66 adds about 5 sec. That extra time ' +
'is eliminated if you view the FG tab, or if you drag this Reports tab to become a ' +
'separate window.
');
writeln(''); // required for line breaks
writeln('');
writeln('No report yet.');
writeln('
');
writeln('');
writeln('');
writeln('');
}
// initialize clock
startElapsed = new Date();
}
var thisElapsed = elapsedSeconds(startElapsed);
var red1 = "";
var red2 = "";
if (thisElapsed - previousElapsed > 5.0)
{
red1 = "";
red2 = "";
previousRedElapsed = thisElapsed;
}
previousElapsed = thisElapsed;
if (thisElapsed - previousRedElapsed > 5.0)
{
red1 = "";
red2 = "";
previousRedElapsed = thisElapsed;
}
reportElapsedText += "\n" + red1 + thisElapsed + red2 + " sec: " + msg;
if (match(msg, "busy-off")) reportElapsedText += "\n";
reportElapsedWindow.document.getElementById("elapsedDiv").innerHTML = reportElapsedText;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function elapsedSeconds(stim)
{
var thisTime = new Date();
var elapsed = thisTime - stim;
elapsed /= 100; // millisec to tenths of sec
elapsed = Math.round(elapsed);
elapsed /= 10; // tenths sec to sec with one decimal
return (Number(elapsed).toFixed(1));
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* purpose ??? not called
function reportNoElapsed(msg)
{
if (!reportElapsedTimes) return;
reportElapsedText += "\n " + msg;
reportElapsedWindow.document.getElementById("elapsedDiv").innerHTML = reportElapsedText;
}
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* DEFINE JMOL FUNCTIONS */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// NEW FOR 4.2 USING BOBs CODE.
// See conformations.htm!
// Total Locations* Range of altloc codes
// ------------------- ---------------------
// PDB Confs Old New Up by Old New Added Improved
// 3eoj AU 3 878 927 49 A-D A-F E,F +
// 6ss1 AU 11,351 11,359 8 A-D A-D none +
// 4nia AU 5 10,174 same 0 A-D,H same none -
// 5qye
// * Includes singleton location count. Same as altLocCount.
var defineAltLoc1Function =
'\n\
conf = []; \n\
altLocAtoms = {}; // associative array { "[SER]22:B.OG" : ({984 985}), ...} \n\
locsPerAtomBitset = []; \n\
atomNthLocs = []; // array of bitsets \n\
configCap = 6; // maximum number of configs \n\
\n\
function altLoc1() \n\
{ \n\
~configCount = 1; \n\
\n\
# GET LOCATIONS/ATOM \n\
# Atoms with altloc codes but only a single location. \n\
locsPerAtomBitset[1] = {config=0 and altloc != ""}; \n\
\n\
# Locations for atoms with multiple locations/atom. \n\
altLocMultBitset = {%? and not config=0}; # All multiple locations. \n\
\n\
# Get associative array altLocAtoms[] { "[SER]22:B.OG" : ({984 985}), ...} \n\
for (var ata in altLocMultBitset) \n\
{ \n\
var atname = ata.atom; \n\
if (altLocAtoms[atname] == null) \n\
{ \n\
altLocAtoms[atname] = ({}); \n\
} \n\
altLocAtoms[atname] |= ata.atomIndex; \n\
} \n\
\n\
# Get locations for atoms with exactly 2, 3, 4, 5, 6 locations/atom. \n\
# and get ~configCount \n\
for (var key in altlocAtoms.keys) \n\
{ \n\
var locs = altlocAtoms[key]; \n\
var len = locs.length; \n\
if (len > configCap) \n\
{ \n\
javascript locsPerAtomExceedsCap = true; \n\
len = configCap; \n\
} \n\
if (locsPerAtomBitset[len] == null) {locsPerAtomBitset[len] = ({})}; \n\
locsPerAtomBitset[len] |= locs; \n\
if (len > ~configCount) \n\
~configCount = len; \n\
} \n\
\n\
# Initialize array of bitsets \n\
for (var nth = 1; nth <= configCap; nth++) {atomNthLocs[nth] = ({});} \n\
\n\
# Get atomNthLocs[N]. N=1=1st locs of multiple loc atoms. N=2=2nd locs of multiloc atoms. \n\
for (var atname in altLocAtoms) // { "[SER]22:B.OG" : ({984 985}), ...} \n\
{ \n\
// atname is an atom \n\
var locs = altLocAtoms[atname]; // bitset of altlocs for this atom \n\
var lpa = locs.length; // lpa = locs/atom. \n\
# if (lpa == 1) {continue} // we have already eliminated singletons in altLocAtoms[]. \n\
if (lpa > configCap) \n\
{ \n\
javascript locsPerAtomExceedsCap = true; \n\
lpa = configCap; // Put 7th+ locations in 6th locations. No known cases. \n\
} \n\
for (var bsn = 1; bsn <= lpa; bsn++) \n\
{ \n\
atomNthLocs[bsn] |= altLocAtoms[atname][bsn]; \n\
} \n\
} \n\
\n\
// DEFINE CONFORMATIONS AND ATOMS-NOT-REPRESENTED-IN[N] \n\
conf[1] = {%} or locsPerAtomBitset[1] or atomNthLocs[1]; \n\
conf[2] = {%} or locsPerAtomBitset[1] or atomNthLocs[2]; \n\
for (i=3; i <=~configCount; i++) \n\
{ \n\
# For each configuration >2, get fill-in atoms atomsNotRepIn[N] \n\
getNotRep(i); # populates atomsNotRepIn[N] \n\
} \n\
if (~configCount >= 4) {atomsNotRepIn[4] |= atomsNotRepIn[3];} \n\
if (~configCount >= 5) {atomsNotRepIn[5] |= atomsNotRepIn[4];} \n\
if (~configCount >= 6) {atomsNotRepIn[6] |= atomsNotRepIn[5];} \n\
for (i=3; i <=~configCount; i++) \n\
{ \n\
conf[i] = {%} or locsPerAtomBitset[1] or atomNthLocs[i] or atomsNotRepIn[i]; \n\
// syntax: select {@{conf[1]}} \n\
} \n\
} \n\
\n\
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n\
atomsNotRepIn = []; \n\
\n\
function getNotRep(lvl) \n\
{ \n\
byseq = []; \n\
# Load byseq with (lvl)th locs of multloc atom labels at each sequence number. \n\
# For 3eoj AU this is 17 locations. \n\
for (var i in atomNthLocs[lvl]) \n\
{ \n\
atseq = i.label("%r"); \n\
if (byseq[atseq].type == "string") byseq[atseq] = []; \n\
byseq[atseq].push(i.label("[%n]%r:%c.%a")); \n\
} \n\
\n\
# 2. GATHER BITSET OF lvl-1 LOCS (376 MULTLOC IN 3EOJ) NOT REPRESENTED at lvl (359 3EOJ). \n\
atomsNotRepIn[lvl] = {none}; \n\
for (var i in atomNthLocs[lvl-1]) \n\
{ \n\
atseq = i.label("%r"); # resno==sequence number. \n\
if (atseq > byseq.length || byseq[atseq].type != "array") # no atseq element in byseq. \n\
{ \n\
# print "Skipping " + atseq; \n\
# atomsNotRepIn[lvl].push(i) PUSH DOES NOT WORK FOR BITSETS \n\
atomsNotRepIn[lvl] |= i; \n\
continue; \n\
} \n\
# print "------------ Doing " + atseq; \n\
addme = i.label("[%n]%r:%c.%a"); \n\
for (var j in byseq[atseq]) # i=atomindex, j=atomlabel.\n\
{ \n\
#print "" + addme + " / " + j \n\
if (addme == j) {#print "Rep " + addme; addme = ""; break;} \n\
} \n\
if (addme) {#print "NotRep " + addme; atomsNotRepIn[lvl] |= i;} \n\
} \n\
} \n\
';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// for coloring alternate location altloc configurations
var defineColorByConfigsFunction =
'function colorConfig(cfg, thecolor) \n\
{ \n\
select {@{atomNthLocs[cfg]}} \n\
color @thecolor; \n\
color cartoon @thecolor; \n\
color backbone @thecolor; \n\
color trace @thecolor; \n\
} \n\
\n\
function colorByConfigs() # Jmol function! \n\
{ \n\
define ~seltmp selected\n\
select % \n\
color white \n\
color cartoon white \n\
color backbone white \n\
color trace white \n\
\n\
select @{locsPerAtomBitset[1]} \n\
color pink \n\
color cartoon pink \n\
color backbone pink \n\
color trace pink \n\
\n\
colorconfig(1, "red") \n\
colorconfig(2, "orange") \n\
# yellow = disulfide bonds, still yellow on white wireframe \n\
colorconfig(3, "[x00ff00]") # green \n\
colorconfig(4, "[x6080ff]") # blue \n\
colorconfig(5, "[xff00ff]") # magenta \n\
colorconfig(6, "yellow") # \n\
\n\
select ~seltmp \n\
} \n\
';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// n = group name
// R = sequence number
// E = insertion code
// c = chain name
// e = element
// a = atom name (e.g. OD2)
// A = altloc code
// Q = occupancy
// t = temperature
var defineListSaltBridgesFunction =
' sblist = ""; \n\
sbnum = 0; \n\
distmin = 4.0; \n\
distmax = 0.0; \n\
distcount = 0; \n\
distsum = 0.0; \n\
sbpaircount = 0; \n\
sbintercount = 0; \n\
sbtermcount = 0; \n\
\n\
function listSB() \n\
{ \n\
sblist = ""; \n\
oldsbnchain = ""; \n\
sbnum = 0; \n\
distmin = 4.0; \n\
distmax = 0.0; \n\
distcount = 0; \n\
distsum = 0.0; \n\
sbpaircount = 0; \n\
sbintercount = 0; \n\
sbtermcount = 0; \n\
notefield = ""; \n\
m1 = ""; \n\
m2 = ""; \n\
g1 = ""; \n\
\
consurf_sb_n_cation = 0; \n\
consurf_sb_n_anion = 0; \n\
consurf_sb_sum_cation = 0; \n\
consurf_sb_sum_anion = 0; \n\
consurf_sb_av_cation = 0; \n\
consurf_sb_av_anion = 0; \n\
consurf_sb_av = 0; \n\
\n\
# SET CONSURFMODE IN JMOL \n\
consurfmode = {protein}.property_consurf.max > 0.1; # max is 9.0 \n\
\n\
for (var sbn in {~targ_sb}) \n\
{ \n\
sbnum++; \n\
# sbnum will be prepended later, conditionally. \n\
var sbnline = "\t" + {sbn}.label("%3n\t%4R\t%3E\t%3c\t"); \n\
# Color chain termini atoms magenta. \n\
var sbnatom = {sbn}.label("%a"); \n\
if (sbnatom.length == 1) \n\
{ \n\
sbnline += m1; \n\
sbtermcount++; \n\
} \n\
sbnline += sbnatom; \n\
if (sbnatom.length == 1) \n\
sbnline += m2; \n\
sbnline += {sbn}.label("\t%3A"); \n\
\n\
# --- CONSURF --- \n\
if (consurfmode) \n\
{ \n\
convaln = ({sbn}.property_consurf)%0; \n\
convaln_s = convaln; \n\
if (convaln < 0) convaln_s = " "; \n\
} \n\
else \n\
{ \n\
convaln_s = " "; \n\
} \n\
sbnline += "\t" + convaln_s; \n\
\n\
var sbnchain = {sbn}.label("%c"); \n\
var oldsbnline = ""; \n\
\n\
# select salt-bridged oxygens FOR THIS SINGLE CATION\n\
# select (asp.od?,glu.oe?,~cont_sb_oterm) and within(4.0, sbn); \n\
select (~cont_sb) and within(4.0, sbn); \n\
select selected or (~cont_sb and *.c) and within(5.2, sbn); \n\
define sbos selected; \n\
\n\
# for each O partner \n\
for (var sbo in {sbos}) \n\
{ \n\
notefield = ""; \n\
sbpaircount++; \n\
sb1 = g1 + sbnum + sbnline + m2; \n\
if (sbnline != oldsbnline) sb1 = "" + sbnum + sbnline; \n\
\n\
sboline = {sbo}.label("%3n\t%4R\t%3E\t"); \n\
\n\
# Boldface chain for between-chain bridges. \n\
if (sbnchain != {sbo}.label("%c")) \n\
{ \n\
sboline += ""; \n\
sbintercount++; \n\
} \n\
sboline += {sbo}.label("%3c"); \n\
if (sbnchain != {sbo}.label("%c")) sboline += "*"; \n\
sboline += "\t"; \n\
\n\
# MAGENTA CHAIN TERMINI ATOMS \n\
var sboatom = {sbo}.label("%a"); \n\
if (sboatom.length == 1 || sboatom == "OXT") # O, C, or OXT \n\
{ \n\
sboline += m1; \n\
sbtermcount++; \n\
# if (sbnline.find(">N<")) {notefield += m1 + "End to end. " + m2} \n\
if (sbnatom == "N") {notefield += m1 + "End to end. " + m2} \n\
else {notefield += m1 + "C-terminus. " + m2} \n\
if (sboatom == "C") {notefield += m1 + "Lacks 1 oxygen.§ " + m2} \n\
} \n\
elseif (sbnatom == "N") {notefield += m1 + "N-terminus. " + m2} \n\
sboline += sboatom; \n\
if (sboatom.length == 1 || sboatom == "OXT") \n\
sboline += m2; \n\
\n\
sboline += {sbo}.label("\t%3A"); \n\
\n\
# --- CONSURF N --- \n\
if (consurfmode) \n\
{ \n\
if (convaln > 0) \n\
{ \n\
consurf_sb_n_cation++; \n\
consurf_sb_sum_cation += convaln; \n\
} \n\
} \n\
\n\
# --- CONSURF O --- \n\
if (consurfmode) \n\
{ \n\
convalo = ({sbo}.property_consurf)%0; \n\
if (convalo > 0) \n\
{ \n\
consurf_sb_n_anion++; \n\
consurf_sb_sum_anion += convalo; \n\
} \n\
if (convalo < 0) convalo = " "; \n\
} \n\
else \n\
{ \n\
convalo = " "; \n\
} \n\
sboline += "\t" + convalo; \n\
\n\
if (sbnchain != {sbo}.label("%c")) notefield += "Between chains.* "; \n\
\n\
sboline += "\t" + notefield; \n\
\n\
sbdist = {sbo}.xyz.distance({sbn}.xyz); \n\
if (sboatom != "C") \n\
{ \n\
distsum += sbdist; \n\
distcount++; \n\
if (sbdist < distmin) distmin = sbdist; \n\
if (sbdist > distmax) distmax = sbdist; \n\
} \n\
\n\
# insert blank line before new N chain \n\
if (sbnchain != oldsbnchain && sbnum != 1) sblist += "
"; \n\
oldsbnchain = sbnchain; \n\
gg1 = ""; \n\
gg2 = ""; \n\
if (sboatom == "C") {gg1=g1;gg2=m2;} \n\
\n\
sblist += sb1 + "\t" + gg1 + format("%1.1f", sbdist) + gg2 + "\t" + sboline + "\n"; \n\
\n\
oldsbnline = sbnline; \n\
} # end o atoms \n\
} # end n atoms \n\
consurf_sb_av_cation = q1b(consurf_sb_sum_cation, consurf_sb_n_cation); \n\
consurf_sb_av_anion = q1b(consurf_sb_sum_anion, consurf_sb_n_anion); \n\
consurf_sb_av = q1b((consurf_sb_sum_anion + consurf_sb_sum_cation), \
(consurf_sb_n_anion + consurf_sb_n_cation)); \n\
}';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var defineListSBStatsFunction =
'sbliststats = ""; \n\
\n\
function listSBStats() \n\
{ \n\
sbliststats = "
"; \n\
distav = distsum / distcount; \n\
sbliststats += "Average distance between " + distcount + " salt-bridged atom pairs: "; \n\
sbliststats += format("%1.1f Å", distav) + ".
"; \n\
sbliststats += "(Minimum " + format("%1.1f", distmin) + ", "; \n\
sbliststats += "Maximum " + format("%1.1f", distmax) + ")
"; \n\
}\n\
\
function addFoundConsurf() \n\
{ \n\
# SET CONSURFMODE IN JMOL \n\
consurfmode = {protein}.property_consurf.max > 0.1; # max is 9.0 \n\
\n\
var atat = 1; # 0th element is last element! \n\
var atcon, atcons; \n\
consurf_found_n = 0; \n\
consurf_found_sum = 0; \n\
for (var fcon in {selected}) \n\
{ \n\
if (consurfmode) \n\
{ \n\
atcon = {fcon}.property_consurf%0; \n\
atcons = atcon; \n\
if (atcon < 0) atcons = " "; \n\
} \n\
else {atcons = " ";} \n\
#listFoundAtoms[atat] += "\t" + atcon // += MESSES UP ARRAY -- UNCLEAR WHY. \n\
listFoundAtoms[atat] = listFoundAtoms[atat] + "\t" + atcons; \n\
atat++; \n\
\
if (atcon > 0) \n\
{ \n\
consurf_found_n++; \n\
consurf_found_sum += atcon; \n\
} \n\
} \n\
consurf_found_av = q1b(consurf_found_sum, consurf_found_n); \n\
} \n\
';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var defineDebugg =
'function debugg() \n\
{ \n\
print "listSBTerms" \n\
} \n\
\n\
sb_terms = [ \n\
["~targ_sb_noends" "SC N->SC O & SC O->SC N"] \n\
["~cont_sb_noends" ""] \n\
\n\
["~targ_sb_nterm" "Nterm to Oterm or SC O"] \n\
["~cont_sb_nterm" ""] \n\
["~targ_sb_n2oterm" "SC N to Oterm"] \n\
["~cont_sb_n2oterm" ""] \n\
\n\
["~targ_sb_oterm" "Oterm to Nterm or SC N"] \n\
["~cont_sb_oterm" ""] \n\
["~targ_sb_o2nterm" "SC O to Nterm"] \n\
["~cont_sb_o2nterm" ""] \n\
\n\
["~cterm_sb_nooxt_carbons" ""] \n\
["~cterm_sb_nooxt_cations" ""] \n\
\n\
["~targ_sb" "All Target SB Atoms"] \n\
["~cont_sb" "All Contacting SB Atoms"] \n\
]; \n\
\n\
function listSBTerms() \n\
{ \n\
for (isb in sb_terms) listAtoms(isb[1], isb[2]); \n\
} \n\
\n\
function listAtoms(aterm, ais) # aterm is defined term quoted as a string \n\
{ \n\
print aterm + " " + ais; \n\
if ({@aterm}.count) print {@aterm}.identify.all; \n\
print ""; \n\
}\n';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var defineJSA =
'function jsa(xxpr)\n\
{\n\
cmd = \'javascript alert(\' + xxpr + \');\';\n\
script inline @cmd;\n\
}\n'; //'
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// returns the single line containing the key, or a blank string if key is not present.
// WORKS BUT NOT IN USE
/*
var defineFilePeek =
'function filepeek(pkurl)\n\
{\n\
return (load(pkurl, 500).lines.find("consurf_pdb_id"));\n\
}\n'; //'
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function getBetween(src, d1, d2, pos)
{
var i1, i2;
i2 = 0;
for (var ipos = 1; ipos <= pos; ipos++)
{
i1 = src.indexOf(d1, i2);
if (i1 == -1) return "";
i2 = i1 + 1;
}
var i2 = src.indexOf(d2, i1);
//alert(i1 + " " + i2);
return src.substring(i1 + d1.length, i2);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function getBetween(src, d1, d2)
{
var i1 = src.indexOf(d1);
if (i1 == -1) return "";
var i2 = src.indexOf(d2, i1);
//alert(i1 + " " + i2);
return src.substring(i1 + d1.length, i2);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' */
function chainIsPresent(ccc) // returns Boolean.
{
// We'll assume that ccc is a single-character chain name from the asymmetric unit,
// or a multiple-character name from the biomolecule.
// When viewing the AU, if it has A-J, we'll assume ccc is one of A-J.
// Therefore all are present.
if (forceAU) return true;
for (icl = 0; icl < bmChainsTotal; icl++)
{
if (ccc.charAt(0) == bmChainList[icl].charAt(0)) return true; // case-dependent.
}
return false;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// When "A" is absent but "A1" is present, calling this function with "A"
// will return "A1". If no chain whose name starts with "A.." is present,
// returns blank.
function firstChainInFamily(ccc)
{
var fam1 = "";
for (var iclx = 0; iclx < bmChainList.length; iclx++)
{
// alert(iclx + " " + ccc + " " + bmChainList[iclx]);
// if this chain name's first character is ccc
if (bmChainList[iclx].charAt(0) == ccc)
{
fam1 = bmChainList[iclx];
break;
}
}
return fam1;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Surely this is unnecessary! Would a BM ever be missing one of the sequence familes
// present in the AU? YES: for 8q03, BM1 has A=B, BM2 has C=C, and AU has A=B distinct
// from C. Chain C is almost identical to A=B: it is missing the HIS tag,
// and has one other single AA difference.
function getBMDistinctChainCount()
{
bmDistinctChainCount = chainList.length;
if (bmNumber == 0) return;
// Deduct any chain families not present in the biomolecule.
// For each chain family in the AU
for (var icf=0; icf < chainList.length; icf++)
{
// For each chain in this AU family
var fampresent = false;
for (var icat=0; icat < chainList[icf].length; icat++)
{
if (firstChainInFamily(chainList[icf].charAt(icat)).length > 0)
{
fampresent = true;
break;
}
}
if (!fampresent) bmDistinctChainCount--;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
alertSlablock = true;
function fgMeasureCallback(p1, p2, p3, p4)
{
//alert(p1 + " | " + p2 + " | " + p3 + " | " + p4);
// p4 = measurePending or measureCompleted
if (match(p4, "measurepending") && toggleIsDown[slabIndex] && !slabLock && alertSlablock)
{
var amsg = "";
if (match(helpPanel, "crosslink")) amsg +=
"Please use SLAB SETTINGS to CHECK\n" +
"ROTATE SLAB before making measurements.\n";
else amsg +=
"Please check ROTATE SLAB (in the Slab\n" +
"dialog) before making measurements.\n";
amsg +=
"Otherwise, the distance/angle may be\n" +
"outside the slab and invisible.";
alert(amsg);
}
alertSlablock = false;
setTimeout("alertSlablock = true", 7000);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function blankBottomEcho()
{
runBareSpt('set echo bottom left;echo "";\n');
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function simplifiedNoAltLoc()
{
msg = "";
if (preferenceDetails)
{
msg += '' + rarr +
'' +
'Alternate locations/occupancies, if specified, are not available ' +
'because this model has been ' +
'simplified. ';
if (simpOpt)
{
msg += 'Try reloading, and decline the option to simplify. ';
}
else if (validPDBId != "" && bmOverAU > 1)
{
msg += ' Try the ' +
'smaller asymmetric unit.';
}
}
return msg;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function simplifiedNoCrosslinks()
{
msg = "";
msg += '' + rarr + 'Protein crosslinks, when present, ' +
'will not be detected ' +
'because this model has been ' +
'simplified. ';
getBMOverAU();
if (simpOpt)
{
msg += 'Try reloading, and decline the option to simplify. ';
}
else if (validPDBId != "" && bmOverAU > 1)
{
msg += ' Try the ' +
'smaller asymmetric unit.';
}
return msg;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// listAltLocs() is for debugging. It must be called manually.
defineListAltLocs =
'function listAltLocs() \n\
{ \n\
var aal = {%?}; \n\
var aalc = {aal}.count; \n\
for (var ai=1; ai < aalc; ai++) \n\
{ \n\
ata = aal[ai]; \n\
atai = ata.atomindex; \n\
print {atomindex=atai}.format("%n%r:%c.%a%%%A"); \n\
} \n\
} \n\
';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Sets ~bbForksBool true if any *.CA/*.P %A%B pairs (first 2 configs only) deviate by > 3 Å.
// Gathers fork deviation stats.
// EXAMPLES:
// 4NIA HAS LOTS OF %? BUT MAX DEVIATION OF 0.18 AND NO CONSEC PAIRS.
// 7RIN has devs >5 Å.
// NOTE that altlocMultBitset has all configs.
// altlocKeys = {~camult}.altloc.pivot.keys ALTLOC CODES UNNEC just use config=2
var defineForkDevsJmol =
' \n\
// Do not use var for externals!! \n\
~bbForksBool = false; \n\
forkDeviationThreshold = 1.5; \n\
var devlistp, devsump, devlistn, devsumn; \n\
\n\
function getForkDevs(mode) // mode is "p" or "n" for protein/nucleic \n\
{ \n\
var prot = true; // protein \n\
if (mode == "n") // nucleic \n\
{ \n\
prot = false; \n\
} \n\
var devav, devmin, devmax; \n\
var altlocKeysCount = {%?}.altloc.pivot.keys.count; \n\
var CACount = {calpha}.count; \n\
var CAC1Count = {calpha and conformation1}.count; \n\
var PCount = {*.p and nucleic}.count; \n\
var PC1Count = {*.p and nucleic and conformation1}.count; \n\
\n\
# Need to run this function BEFORE clicking on altloc and defining altlocMultBitset. \n\
// var ~camult1 = {altlocMultBitset and *.ca and config=1} \n\
var ~camult1; \n\
// These include singletons! \n\
//if (prot) {~camult1 = {%? and calpha and conformation1};} \n\
//else {~camult1 = {%? and *.p and nucleic and conformation1};} \n\
if (prot) {~camult1 = {~bbforks1 and calpha};} \n\
else {~camult1 = {~bbforks1 and phosphorus};} \n\
var camult1c = {~camult1}.count; \n\
\n\
var cai, at1, atai1, atid1, at2, at2c, atai2, dev; \n\
var bbtot, bbpct; \n\
var prevseq, nonconseq; \n\
var devtot = 0; \n\
var pairs = 0; \n\
devmin = 10000.0; \n\
devmax = 0.0; \n\
var devlist = ""; \n\
var devsum = "\n"; \n\
\n\
// Zeroth element of bitset is last element. Start at 1. \n\
for (cai = 1; cai <= camult1c; cai++) \n\
{ \n\
at1 = ~camult1[cai]; \n\
atai1 = at1.atomindex; \n\
atid1 = "" + {atomindex=atai1}.label("%n%r:%c.%a"); \n\
\n\
at2 = {@atid1 and config=-2}; \n\
at2c = at2.count; \n\
if (at2c) \n\
{ \n\
pairs++; \n\
atai2 = at2.atomindex; \n\
\n\
dev = distance({atomindex=atai1},{atomindex=atai2})%2; \n\
if (dev >= forkDeviationThreshold) ~bbForksBool = true; \n\
devtot += dev; \n\
if (dev < devmin) devmin = dev; \n\
if (dev > devmax) devmax = dev; \n\
\n\
nonconseq = "§"; \n\
if (cai != 1 && (0 + at1.label("%r")) == (prevseq + 1)) {nonconseq = ""}; \n\
\n\
devlist += "" + pairs + "\t" + at1.label("%n\t%r\t" + nonconseq + "\t%c\t%a\t%%%A") + "\t" + \n\
at2.label("%n\t%r\t%c\t%a\t%%%A") + "\t" + \n\
dev + "\n"; \n\
\n\
prevseq = (0 + at1.label("%r")); \n\
} // end if \n\
} // end for \n\
\n\
var bbatoms = "alpha carbons"; \n\
if (!prot) {bbatoms = "nucleic phosphorus atoms";} \n\
//devsum += " - "; \n\
devsum += "- "; \n\
\n\
if (!pairs) \n\
{ \n\
if (prot) \n\
{ \n\
if (CACount) // N.B. alphaCarbonCount is JS, this is JSmol \n\
{ \n\
devsum += "Zero " + bbatoms + " have alternate locations.
\n"; \n\
} \n\
else \n\
{ \n\
devsum += "There is no protein in this model.
\n"; \n\
} \n\
} \n\
else // mode is nucleic \n\
{ \n\
if (PCount) \n\
{ \n\
devsum += "Zero " + bbatoms + " have alternate locations.
\n"; \n\
} \n\
else \n\
{ \n\
devsum += "There are no nucleic acids in this model.
\n"; \n\
} \n\
} \n\
} \n\
else \n\
{ \n\
devav = (devtot/pairs)%2; \n\
if (prot) {bbtot = CAC1Count;} \n\
else {bbtot = PC1Count;} \n\
bbpct = ((100.0 * pairs)/(1.0 * bbtot))%1; \n\
devsum += "" + pairs + " " + bbatoms + " " + \n\
"(" + bbpct + "% of all " + bbtot + " in conformation 1) " + \n\
"have alternate locations"; \n\
if (altlocKeysCount <= 2) {devsum += ".\n"}; \n\
else {devsum += "\nin the first two conformations.\n"}; \n\
devsum += \n\
"Separations between those two alternate locations of " + bbatoms + ":
\n" + \n\
" Min " + \n\
devmin + ", Average " + devav + ", Max " + devmax + " Å.
\n"; \n\
} \n\
\n\
// using // or # in next line CAUSES SCRIPT TO FAIL! \n\
/*devsum += "~bbForksBool = " + ~bbForksBool + "\n";*/\n\
\n\
devsum += "
\n"; \n\
if (prot) \n\
{ \n\
devsump = devsum; \n\
devlistp = devlist; \n\
} \n\
else \n\
{ \n\
devsumn = devsum; \n\
devlistn = devlist; \n\
} \n\
//print devsum; \n\
} \n\
';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// FOR 4.2, DECIDED NOT TO USE CONSECUTIVE PAIRS AS A TEST FOR FORKS.
// PLAN TO SIMPLY SEE IF ANY *.CA %A%B PAIRS DEVIATE BY > 3 Å.
// AND REPORT DEVIATION STATISTICS.
// GOOD EXAMPLE OF NO FORKS
// IS 4NIA WHICH HAS LOTS OF %? BUT MAX DEVIATION OF 0.18 AND NO CONSEC PAIRS.
// Returns true if two bb atoms in consecutive residues have altlocs, either
// alpha carbons or P's.
// NOTE that altlocMultBitset has all configs.
// altlocKeys = {~camult}.altloc.pivot.keys ALTLOC CODES UNNEC just use config=2
/*
//var doubleForkedJSmol =
' \n\
~camult = {altlocMultBitset and *.ca}; \n\
minAltLocDeviation = 1.0; \n\
\n\
//function doubleForked() \n\
{ \n\
var ~camultc = {~camult}.count; \n\
\n\
var atcam, atai, atal, foundatoms, foundc; \n\
\n\
for (var cai = 0; cai <= ~camultc; cai++) \n\
{ \n\
atcam = ~camult[cai]; \n\
atai = atcam.atomindex; \n\
atal = atcam.altloc; \n\
foundatoms = {~camult and within(3.9, atcam) and (atomindex > atai) and\
altloc==atal}; \n\
foundc = foundatoms.count; \n\
if (foundc) \n\
{ \n\
\n\
} \n\
} \n\
} \n\
';
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' */
function setBBForks()
{
// alert("util.js #9: alphaCarbonCount=" + alphaCarbonCount + "\n" +
// "nucleicPhosphorusCount=" + nucleicPhosphorusCount);
// alert("util.js #10: starting JSmol function getForkDevs()");
if (!altLocForkDevsGotten)
unchangedScriptToJmol("getForkDevs('p');getForkDevs('n');");
altLocForkDevsGotten = true;
setBBForks2();
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function setBBForks2()
{
if (jmolEvaluate("devsumn").length) // JSmol getForkDevs('n') has completed.
{
// alert("util.js #11: JSmol function getForkDevs() FINISHED.");
// Not used in 4.2 because this takes too long.
//bbForks = jmolEvaluate("~bbForksBool"); // deviation >= forkDeviationThreshold.
// alertForks() is called elsewhere.
altLocBBDeviationSummary = jmolEvaluate("devsump") + jmolEvaluate("devsumn");
// devlistp, devlistn to be fetched if requested.
document.getElementById("forkstats").innerHTML = altLocBBDeviationSummary +
'' +
'
' +
'' +
' List all backbone atom separations between the ' +
'first two conformations. Additional sets can be listed after
Finding them ' +
'in the table below.
';
}
else
{
setTimeout("setBBForks2()", 25);
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function showBBForkStats(say)
{
if (say)
{
document.getElementById("forkstats").innerHTML =
'Distances will appear here when ready ...';
}
setTimeout("setBBForks()", 30);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var newTabsForeground =
'\n\
New browser tabs are opened by many actions in FirstGlance in Jmol.\n\
If they get lost in the background among your other tabs, set your browser to\n\
\n\
bring them to the front automatically.';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var animTooBig =
'' +
'500 pixels is usually plenty for presentations. ' +
// 'Larger may give jerky animation on older computers.'; 4.1
'Larger sizes will be reduced by the animated GIF generator. ' +
'You can make larger animations with the ' +
'Animation Kit. ' +
'';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function amrLink()
{
lhtml = ' Amr Alhossary ';
return(lhtml);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// n = group name
// R = sequence number
// E = insertion code
// c = chain name
// e = element
// a = atom name (e.g. OD2)
// A = altloc code
// Q = occupancy
// t = temperature
var defineListContactsFunction =
// BOND TYPES:
// HBond hydrogen bonds (including water and water bridges): N,O ≤3.5 Å.
// Apolar apolar: between C, S, Se ≤4.0 Å.
// SaltBr salt bridge: sidechain N on Lys/Arg or N-terminal N to
// sidechain O on Glu/Asp or C-term O.
// CatPi cation pi: ring C in Phe, Tyr, Trp ≤ 6 Å? from sidechain N on Lys/Arg or N-terminal N.
'function q1b(numr, denom) // quotient with 1 decimal place, or blank. \n\
{ \n\
if (denom == 0) {return " "}; // avoid division by zero! \n\
else {return ((0.0 + numr)/denom%1)}; // cast integer to float! \n\
} \n\
\
// Do not use var for externals!! \n\
contactslist = ""; \n\
\
consurf_targ_sum = 0; # for averaging \n\
consurf_targ_count = 0; # for averaging \n\
consurf_cont_sum = 0; # for averaging \n\
consurf_cont_count = 0; # for averaging \n\
\
consurf_av_targ_hbonds = 0; \n\
consurf_n_targ_hbonds = 0; \n\
consurf_av_targ_hphob = 0; \n\
consurf_n_targ_hphob = 0; \n\
consurf_av_targ_sb = 0; \n\
consurf_n_targ_sb = 0; \n\
consurf_av_targ_catpi = 0; \n\
consurf_n_targ_catpi = 0; \n\
\
consurf_av_cont_hbonds = 0; \n\
consurf_n_cont_hbonds = 0; \n\
consurf_av_cont_hphob = 0; \n\
consurf_n_cont_hphob = 0; \n\
consurf_av_cont_sb = 0; \n\
consurf_n_cont_sb = 0; \n\
consurf_av_cont_catpi = 0; \n\
consurf_n_cont_catpi = 0; \n\
\n\
consurf_n_catpi_cat = 0; \n\
consurf_sum_catpi_cat = 0; \n\
consurf_av_catpi_cat = 0; \n\
consurf_n_catpi_pi = 0; \n\
consurf_sum_catpi_pi = 0; \n\
consurf_av_catpi_pi = 0; \n\
\n\
paircount_cov = 0; \n\
paircount_hbonds = 0; \n\
paircount_hphob = 0; \n\
paircount_sb = 0; \n\
paircount_catpi = 0; \n\
paircount_metmisc = 0; \n\
paircount_dpclash = 0; \n\
\
\
function listContacts() # JSmol function \n\
{ \n\
define ~seltmp selected; \n\
contactslist = ""; \n\
oldtargchain = ""; \n\
contpaircount = 0; \n\
bondpaircount = 0; \n\
contintercount = 0; \n\
notefield = ""; \n\
\n\
# SET CONSURFMODE IN JMOL \n\
consurfmode = {protein}.property_consurf.max > 0.1; # max is 9.0 \n\
\n\
# COVALENT \n\
# In 1hho, each AU dimer has PO4. In the tetramer, the two PO4s are superposed. \n\
# This creates bogus "covalent" bonds between them. So eliminate atoms in ~samespace. \n\
# For 1hho, 0.0 did not work. 0.1 worked. Using 0.5 to be safe. \n\
select ~outside_contarget and within(0.5, ~contarget); \n\
define ~samespace selected; \n\
\
select ~contarget; \n\
define ~targatoms selected; \n\
select ~outside_contarget and within(1.75, ~contarget); \n\
select selected and not ~samespace; \n\
define ~contatoms selected; \n\
bondtype = "Cov."; \n\
distmax = 1.75; \n\
getContactPairs(); \n\
paircount_cov = bondpaircount; \n\
\n\
# HBONDS \n\
select ~targ_hbonds; \n\
define ~targatoms selected; \n\
select ~cont_hbonds and not ~samespace; \n\
define ~contatoms selected; \n\
bondtype = "Hbond"; \n\
distmax = 3.5; \n\
getContactPairs(); \n\
paircount_hbonds = bondpaircount; \n\
consurf_n_targ_hbonds = consurf_targ_count; \n\
consurf_n_cont_hbonds = consurf_cont_count; \n\
consurf_av_targ_hbonds = q1b(consurf_targ_sum, consurf_targ_count); \n\
consurf_av_cont_hbonds = q1b(consurf_cont_sum, consurf_cont_count); \n\
\n\
# APOLAR \n\
select ~targ_hphob; \n\
define ~targatoms selected; \n\
select ~cont_hphob and not ~samespace; \n\
define ~contatoms selected; \n\
bondtype = "Apolar"; \n\
distmax = 4.0; \n\
getContactPairs(); \n\
paircount_hphob = bondpaircount; \n\
consurf_n_targ_hphob = consurf_targ_count; \n\
consurf_n_cont_hphob = consurf_cont_count; \n\
consurf_av_targ_hphob = q1b(consurf_targ_sum, consurf_targ_count); \n\
consurf_av_cont_hphob = q1b(consurf_cont_sum, consurf_cont_count); \n\
\n\
# SALT BRIDGES \n\
select ~targ_sb; \n\
define ~targatoms selected; \n\
select ~cont_sb and not ~samespace; \n\
define ~contatoms selected; \n\
bondtype = "SaltBr."; \n\
distmax = 4.0; \n\
getContactPairs(); \n\
paircount_sb = bondpaircount; \n\
consurf_n_targ_sb = consurf_targ_count; \n\
consurf_n_cont_sb = consurf_cont_count; \n\
consurf_av_targ_sb = q1b(consurf_targ_sum, consurf_targ_count); \n\
consurf_av_cont_sb = q1b(consurf_cont_sum, consurf_cont_count); \n\
\n\
# CATION PI \n\
select ~catpi_in_target; \n\
define ~targatoms selected; \n\
select ~catpi_contacting_target and not ~samespace; \n\
define ~contatoms selected; \n\
bondtype = "CatPi"; \n\
distmax = 6.0; \n\
getContactPairs(); \n\
paircount_catpi = bondpaircount; \n\
consurf_n_targ_catpi = consurf_targ_count; \n\
consurf_n_cont_catpi = consurf_cont_count; \n\
consurf_av_targ_catpi = q1b(consurf_targ_sum, consurf_targ_count); \n\
consurf_av_cont_catpi = q1b(consurf_cont_sum, consurf_cont_count); \n\
consurf_av_catpi_cat = q1b(consurf_sum_catpi_cat, consurf_n_catpi_cat); \n\
consurf_av_catpi_pi = q1b(consurf_sum_catpi_pi, consurf_n_catpi_pi); \n\
\n\
# METAL AND MISCELLANEOUS \n\
select ~targ_memi; \n\
define ~targatoms selected; \n\
select ~cont_memi and not ~samespace; \n\
define ~contatoms selected; \n\
bondtype = "MetMisc"; \n\
distmax = 6.0; \n\
getContactPairs(); \n\
paircount_metmisc = bondpaircount; \n\
\n\
# DEEP CLASHES \n\
select ~contarget; \n\
define ~targatoms selected; \n\
select ~samespace; \n\
define ~contatoms selected; \n\
bondtype = "DpClash"; \n\
distmax = 0.5; \n\
getContactPairs(); \n\
paircount_dpclash = bondpaircount; \n\
\
select on ~seltmp; \n\
} \n\
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n\
consurf_targ_count = 0; \n\
consurf_cont_count = 0; \n\
consurf_targ_sum = 0; \n\
consurf_cont_sum = 0; \n\
#cont_debug = "" \n\
\n\
function getContactPairs() \n\
{ \n\
#cont_debug = {~contatoms}.identify \n\
\n\
var convaltarg; \n\
var convalcont; \n\
consurf_targ_count = 0; \n\
consurf_cont_count = 0; \n\
consurf_targ_sum = 0; \n\
consurf_cont_sum = 0; \n\
bondpaircount = 0; \n\
\
# SET CONSURFMODE IN JMOL \n\
consurfmode = {protein}.property_consurf.max > 0.1; # max is 9.0 \n\
\
for (var targat in {~targatoms}) \n\
{ \n\
var targline = "\t" + {targat}.label("%3n\t%4R\t%3E\t%3c\t%a\t%3A"); # thru Altloc \n\
\n\
# TARGET CONSURF VALUE \n\
var cvt; \n\
if (consurfmode) \n\
{ \n\
convaltarg = {targat}.property_consurf%0; \n\
cvt = convaltarg; # Keep convaltarg intact for use below. \n\
if (convaltarg < 0) cvt = " "; \n\
} \n\
else \n\
{ \n\
cvt = " "; \n\
} \n\
targline += "\t" + cvt; \n\
\n\
var targchain = {targat}.label("%c"); \n\
var targelement = {targat}.label("%e"); \n\
var targaltloc = {targat}.label("%A"); \n\
\n\
select ~contatoms and within(@distmax, {targat}); \n\
define ~contclose selected; \n\
\n\
# For each contacting partner \n\
for (var contat in {~contclose}) \n\
{ \n\
\n\
/* \n\
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \n\
#cont_debug = {~contclose}.identify \n\
if (bondtype == "SaltBr." && targat == {~targatoms}[1] && contat == {~contclose}[1]) \n\
{ \n\
javascript alert(jmolEvaluate("cont_debug")); \n\
} \n\
*/ \n\
\n\
# SaltBr.: Must avoid listing N to N or (O,C) to (O,C) carbons. \n\
# NOTE that unlike for ALL salt bridges (where targ=N and cont=O), for CONTACTS, \n\
# ~targ_sb has both N and (O,C), and ditto for ~cont_sb. \n\
# Example: without this exclusion code, \n\
# 1jyn contacts to chain A lists SaltBr. Arg13:A.NH1 to Arg13:D.NH1. \n\
if (bondtype == "SaltBr.") \n\
{ \n\
# contat is an atomindex. element is upper case. \n\
# A pair of N must not be listed. \n\
if ( # Neither is an N \n\
({targat}.element != "N") \n\
&& \n\
({contat}.element != "N") \n\
) {continue;} \n\
if ( # Neither is an O,C (C for C-terminus without OXT) \n\
({targat}.element != "O" && {targat}.element != "C") \n\
&& \n\
({contat}.element != "O" && {contat}.element != "C") \n\
) {continue;} \n\
} \n\
\n\
# CatPi: Must avoid listing ring-to-ring carbons. One pair-member must be a cation. \n\
# Example: without this exclusion code, \n\
# 1usi contacts to Phe1345 monomeric ligand list CatPi Phe1346.C? to TYR150.C?. \n\
if (bondtype == "CatPi") \n\
{ \n\
# contat is an atomindex. group is upper case. \n\
# A pair of rings must not be listed. \n\
if ( # Neither is a cation \n\
({targat}.group != "LYS" && {targat}.group != "ARG" && {targat}.element != "N") \n\
&& \n\
({contat}.group != "LYS" && {contat}.group != "ARG" && {contat}.element != "N") \n\
) {continue;} \n\
# I dont have an example, but surely also one pair-member must be a ring. \n\
# That is, a pair of cations must not be listed. \n\
if ( # Neither is a ring \n\
({targat}.group != "PHE" && {targat}.group != "TYR" && {targat}.group != "TRP") \n\
&& \n\
({contat}.group != "PHE" && {contat}.group != "LYS" && {contat}.group != "TRP") \n\
) {continue;} \n\
} \n\
\n\
# EXCLUDE CONTACTS TO DIFFERENT CONFORMATIONS. \n\
# >>>NO, LATER I DECIDED THE CLASH-ALTLOC-CONTACTS MUST BE INCLUDED IN THE LISTING, \n\
# JUST AS ALL ALTLOCS MUST BE INCLUDED WHEN DETECTING CROSSLINKS. \n\
# 5SOP IS UNUSUAL IN THAT THE LIGAND IS %B BUT IN CONFORMATION1, YET \n\
# SURROUNDED BY CONFORMATION1 CLASHING WATERS THAT REPORT AS "Cov.". \n\
# Targeting RYI in 5sop reports many altloc clashes. See conformations.htm \n\
#if (targaltloc != {contat}.label("%A")) continue; \n\
# When target atom is in conformation 1, exclude contacts to other conformations. \n\
// if ({targat and conformation1}.count and !{contat and conformation1}.count) {continue} \n\
# Else exclude if targat altloc ID != contat altloc ID. \n\
//if (!{target and conformation1}.count and \
// ({targat}.altloc != {contat}.altloc)) continue; \n\
\n\
notefield = ""; \n\
contpaircount++; \n\
bondpaircount++; \n\
\n\
if (consurfmode) \n\
{ \n\
if (convaltarg > 0) \n\
{ \n\
consurf_targ_count++; \n\
consurf_targ_sum += convaltarg; \n\
if (bondtype == "CatPi") \n\
{ \n\
if (iscat(targat)) \n\
{ \n\
consurf_n_catpi_cat++; \n\
consurf_sum_catpi_cat += convaltarg; \n\
} \n\
if (ispi(targat)) \n\
{ \n\
consurf_n_catpi_pi++; \n\
consurf_sum_catpi_pi += convaltarg; \n\
} \n\
} \n\
} \n\
} \n\
\n\
# TARGLINE -> TARG1 \n\
targ1 = "" + contpaircount + targline; # CAST TO STRING! \n\
\n\
# ---- BEGIN CONTLINE ---- \n\
var contline = {contat}.label("%3n\t%4R\t%3E\t"); \n\
\n\
# Boldface chain for between-chain contacts. \n\
if (targchain != {contat}.label("%c")) \n\
{ \n\
contline += ""; \n\
contintercount++; \n\
} \n\
contline += {contat}.label("%3c"); # CONTACT CHAIN \n\
if (targchain != {contat}.label("%c")) contline += ""; \n\
\n\
contline += {contat}.label("\t%a\t%3A"); # CONTACT ATOM, ALTLOC \n\
\n\
# ---- CONTACT CONSURF VALUE ---- \n\
if (consurfmode) \n\
{ \n\
convalcont = {contat}.property_consurf%0; \n\
if (convalcont > 0) \n\
{ \n\
consurf_cont_count++; \n\
consurf_cont_sum += convalcont; \n\
if (bondtype == "CatPi") \n\
{ \n\
if (iscat(contat)) \n\
{ \n\
consurf_n_catpi_cat++; \n\
consurf_sum_catpi_cat += convalcont; \n\
} \n\
if (ispi(contat)) \n\
{ \n\
consurf_n_catpi_pi++; \n\
consurf_sum_catpi_pi += convalcont; \n\
} \n\
} \n\
} \n\
if (convalcont < 0) convalcont = " "; \n\
} \n\
else \n\
{ \n\
convalcont = " "; \n\
} \n\
contline += "\t" + convalcont; \n\
\n\
if (targchain != {contat}.label("%c")) notefield += "Between chains. "; \n\
contline += "\t" + notefield; \n\
\n\
# GET DISTANCE \n\
contdist = {targat}.xyz.distance({contat}.xyz); \n\
\n\
# insert blank line before new chain \n\
if (targchain != oldtargchain && sbnum != 1) contactslist += "
"; \n\
oldtargchain = targchain; \n\
\n\
# JOIN TARGLINE AND CONTLINE \n\
# INSERTING DISTANCE AND BOND TYPE \n\
contactslist += \
targ1 + "\t" + format("%1.1f", contdist) + "\t" + bondtype + "\t" + contline + "\n"; \n\
} # end contat loop \n\
} # end targat loop \n\
contactslist += "\n"; # blank line between bond types \n\
\
} \n\
\
function iscat(idnum) \n\
{ \n\
var atres = {idnum}.label("%n"); \n\
if (atres == "LYS" || atres == "ARG") return true; \n\
return false; \n\
} \n\
\
function ispi(idnum) \n\
{ \n\
var atres = {idnum}.label("%n"); \n\
if (atres == "PHE" || atres == "TYR" || atres == "TRP") return true; \n\
return false; \n\
} \n\
';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function goFind(what, mode) // mode is "tac" or "all".
{
var kind = "all";
if (mode == "tac") kind = "contacts";
if (what == "sbpos")
newfind = "~alphacarbons and (lys,arg) and ~" + mode + "_sb " +
"/* Cationic (+) amino acids in " + kind + " salt bridges */";
else if (what == "nsbpos")
newfind = "~alphacarbons and (lys,arg) and not ~" + mode + "_sb " +
"/* Cationic (+) amino acids NOT in " + kind + " salt bridges */";
else if (what == "sbneg")
newfind = "~alphacarbons and (asp,glu) and ~" + mode + "_sb " +
"/* Anionic (-) amino acids in " + kind + " salt bridges */";
else if (what == "nsbneg")
newfind = "~alphacarbons and (asp,glu) and not ~" + mode + "_sb " +
"/* Anionic (-) amino acids NOT in " + kind + " salt bridges */";
else if (what == "sb")
newfind = "~alphacarbons and ~" + mode + "_sb " +
"/* Amino acids in " + kind + " salt bridges */";
else if (what == "pos")
newfind =
"~alphacarbons and (arg,lys) " +
"/* Cationic (+) amino acids */";
else if (what == "neg")
newfind =
"~alphacarbons and (asp,glu) " +
"/* Anionic (-) amino acids */";
else if (what == "nsbchg")
newfind = // Jmol "charged" includes his.
"~alphacarbons and (arg,asp,glu,lys) and not ~" + mode + "_sb " +
"/* Charged amino acids NOT in " + kind + " salt bridges */";
//-------------------------
else if (what == "catpipos")
newfind =
"~alphacarbons and (arg,lys) and ~" + mode + "_catpi " +
"/* Cationic (+) amino acids in " + kind + " putative cation-pi interactions */";
else if (what == "ncatpipos")
newfind =
"~alphacarbons and (arg,lys) and not ~" + mode + "_catpi " +
"/* Cationic (+) amino acids NOT in " + kind + " putative cation-pi interactions */";
else if (what == "catpiarom")
newfind =
"~alphacarbons and (phe,tyr,trp) and ~" + mode + "_catpi " +
"/* Aromatic amino acids in " + kind + " putative cation-pi interactions */";
else if (what == "ncatpiarom")
newfind =
"~alphacarbons and (phe,tyr,trp) and not ~" + mode + "_catpi " +
"/* Aromatic amino acids NOT in " + kind + " putative cation-pi interactions */";
else if (what == "catpi")
newfind =
"~alphacarbons and ~" + mode + "_catpi" +
"/* Amino acids in " + kind + " putative cation-pi interactions */";
else if (what == "arom")
newfind =
"~alphacarbons and (phe,tyr,trp) " +
"/* Aromatic amino acids */";
//-------------------------
else if (what == "chg")
newfind = // Jmol "charged" includes his.
"~alphacarbons and (arg,asp,glu,lys) " +
"/* Charged amino acids (excluding His) */";
else if (what == "polunch")
newfind = // Jmol "polar" includes cys.
"~alphacarbons and (asn,gly,his,ser,thr) " +
"/* Uncharged polar amino acids */";
else if (what == "hphob")
newfind = // Tyr is here, not in polunch.
"~alphacarbons and (ala,cys,gly,ile,leu,met,phe,pro,trp,tyr,val) " +
"/* Apolar amino acids */";
else if (what == "all")
newfind =
"~alphacarbons /* All amino acids */";
else
{
amsg = 'Programmer\'s error: \"' + what + '\" is undefined in util.js';
alert(amsg);
return;
}
findIt(newfind);
// document.getElementById("helpDiv").focus(); DOES NOT FOCUS OPENER TAB.
// opener.window.focus(); NO EFFECT.
setTimeout(goFindAlert(), 500);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function goFindAlert()
{
alert("Now, to get the ConSurf averages,\nclick the pink button\n[List Found Atoms]\n" +
"(It will appear After you click OK.\n" +
"You may need to scroll up in the Find.. dialog.");
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var consurfppda =
'- \n\
Introduction to Evolutionary Conservation \n\
explains the example of Rett Syndrome. A detailed \n\
analysis of conservation of enolase includes \n\
the multiple sequence alignment and 3D visualization. \n\
- \n\
How to See Conserved Regions describes three \n\
straightforward methods using the ConSurf Server. \n\
- \n\
Visualizing Conservation in FirstGlance illustrates shortcuts to see\n\
conservation of salt bridges, cation-pi interactions, ligand-binding residues, protein\n\
crosslinking residues, or any residues you specify.\n\
- \n\
Interpreting ConSurf Results helps you decide whether a ConSurf result\n\
needs optimizing in order to answer your questions,\n\
discussing the distribution of conservation\n\
grades and the average pairwise distance.\n\
- \n\
Conservation, Evolutionary briefly discusses \n\
\
- \n\
How ConSurf works and the color scheme it uses. \n\
- \n\
Why gly and pro on protein surfaces are often conserved. \n\
- \n\
Limitations of conservation analysis. \n\
- \n\
Other conservation servers. \n\
\n\
\
- \n\
ConSurfDB vs. ConSurf covers \n\
\
- \n\
Precalculated conservation vs. custom calculation. \n\
- \n\
How to limit a custom calculation to proteins of a single function. \n\
- \n\
How the ConSurfDB and ConSurf servers work. \n\
\n\
\
\n';
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// New in FG 4.4: P, D, R, Hybrid, CHO, Lig+ (± including CHO)
// THIS IS AN ATTEMPT TO DETERMINE COMPOSITION/TYPE USING JMOL'S DEFINITIONS OF
// protein, DNA, RNA, carbohydrate AND COMPARING TOTAL ATOMS IN A CHAIN
// TO ATOMS OF ONE OF THOSE TYPES within a specified chain.
//
// ABANDONED because it gets very complicated taking into account
// polymerlength=0 (same as polymer=0, monomer=0)
// broken chains, dipeptides/nucleotides, oligosaccharides.
// I shall improve getChainType() instead.
/*
//function getChainType7(cname)
{
var cq, ctot, pcat, dcat, rcat, ccat;
var cq = "{chain=" + cname + " && not solvent}.count";
ctot = jmolEvaluate(cq);
cq = "{chain=" + cname + " and protein}.count";
pcat = jmolEvaluate(cq);
if (pcat/ctot > 0.9) return "Protein";
cq = "{chain=" + cname + " and DNA}.count";
dcat = jmolEvaluate(cq);
cq = "{chain=" + cname + " and RNA}.count";
rcat = jmolEvaluate(cq);
if (dcat == ctot) return "DNA";
else if ((dcat/ctot) > 0.80 && rcat == 0) return "DNA including Ligands+";
else if (dcat > 0 && rcat == 0) return "Ligands+ including DNA";
if (rcat == ctot) return "RNA";
else if ((rcat/ctot) > 0.80 && dcat == 0) return "RNA including Ligands+";
else if (rcat > 0 && dcat == 0) return "Ligands+ including RNA";
if (((dcat + rcat)/ctot) > 0.50) return "DNA/RNA Hybrid including Ligands+";
else if (dcat > 0 && rcat > 0) return "Ligands+ including nucleotides";
cq = "{chain=" + cname + " and (carbohydrate)}.count";
ccat = jmolEvaluate(cq);
if (ccat/ctot > 0.97) return "Carbohydrate";
if (ccat > 0) return "Ligands+ Including Carbohydrate";
else return "Ligand+"; // example: 1m06 chain X.
}
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function rangeifyCases(arr)
{
var bothranges;
separateCases(arr); // makes separatedUCase, separatedLCase.
var uranges = rangeify(separatedUCases);
var lranges = rangeify(separatedLCases);
if (lranges[0].length) bothranges = uranges.concat(lranges);
else bothranges = uranges;
return bothranges;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function rangeify(arr)
{
// alphanumeric sort
arr.sort(new Intl.Collator('en',{numeric:true, sensitivity:'case'}).compare);
var ranged = new Array();
var atran = "";
var iprev;
prang = "";
for (iar = 0; iar < arr.length; iar++)
{
if (arr[iar].charAt(0) != prang)
{
// complete previous range
if (prang.length) // if we already started a range ...
{
if (arr[iar - 1] != arr[iprev])
atran += "-" + arr[iar - 1];
ranged.push(atran);
atran = "";
}
// start new range
atran += arr[iar];
iprev = iar;
prang = arr[iar].charAt(0);
}
}
// finish final range
if (arr[iar - 1] != arr[iprev])
atran += "-" + arr[arr.length - 1];
ranged.push(atran);
return ranged;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function isCHO(hetnam)
{
var chocount = jmolEvaluate("{[" + hetnam + "] and carbohydrate}.count");
return (chocount > 0);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function deDupSortedArray(sarr)
{
var ddarr = new Array();
var old = "";
for (var isa = 0; isa < sarr.length; isa++)
{
if (sarr[isa] != old) ddarr.push(sarr[isa]);
old = sarr[isa];
}
return ddarr;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
var oneLetterChainSequence;
function getModelSequence(dbnum)
{
if (noChainIDs) return "";
var atc = modelSequenceChainID;
//alert("util.js #14 entering getModelSequence(" + dbnum + "), modelSequenceChainID=" + atc);
modelSequenceNSR = 0;
modelSequenceNumberDuplicates = 0;
modelSequenceInsertions = 0;
modelSequenceGaps = 0;
modelSequenceGapSum = 0;
modelSequenceGapSumAll = 0;
modelSequenceDecrements = 0;
var gapsize;
var gapgt8 = false;
// 6yrb BU1 has no B, only B4-B7! So get lowest chain ID beginning atc.
var q1 = "{chain='" + atc + "???'}.chain.pivot.keys[1]"; // .min does not work ... ?
atc = "" + jmolEvaluate(q1);
//alert(atc);
var ctype = chainTypeFromNameShort(atc);
var seqspt = "define ~seltmp selected; select chain=" + atc + " and not water;\n" +
"~selseq = script(\"show residues\"); select ~seltmp;";
// Get 1-letter sequence
if (match(ctype, "protein"))
{
var seqspt1 = "define ~seltmp selected; " +
"~atseq1 = {chain=" + atc + " and alpha and conformation1}.sequence.all; select ~seltmp;";
runBareSpt(seqspt1);
oneLetterChainSequence = jmolEvaluate("~atseq1"); // string, newline delimited.
// delete newlines.
oneLetterChainSequence = oneLetterChainSequence.replace(/\n/g, "");
oneLetterChainSequence = oneLetterChainSequence.replace(/\?/g, "X");
}
//alert("runBareSpt to show residues to get sequence in util.js");
runBareSpt(seqspt);
var atseq = jmolEvaluate("~selseq"); // string. end may have newline.
atseq = atseq.replace(/\s+$/, ""); // delete whitespace at end.
atseq = atseq.replace(/\s+/g, " "); // singlularize spaces.
atseq = atseq.replace(/:./g, ""); // delete :X.
atseq = atseq.replace(/\[/g, ""); // delete [.
atseq = atseq.replace(/\]/g, "."); // replace ] with period.
// can't get this to work.
//atseq = atseq.replace(/(\^.+) /g, '$1 '); // ^X -> magenta
var seqarr = atseq.split(" ");
modelSequenceLength = seqarr.length;
var icaret, atr, atrlink, atgroup, seqnum, inscode;
var sprev = null; // == null without quotes is true.
var ul = false;
for (var is=0; is < seqarr.length; is++)
{
atr = seqarr[is];
// Separate name and number.
seqnum = parseInt(atr.substring(atr.indexOf(".") + 1));
atgroup = atr.substring(0, atr.indexOf("."));
inscode = "";
icaret = atr.indexOf("^");
if (icaret != -1)
{
inscode = atr.substring(icaret);
modelSequenceInsertions++;
}
//if (inscode) alert(inscode);
// RESIDUE, bolding non-standard residues.
var linkedgroup = '' + atgroup + '';
if (match(ctype, "protein"))
{
if (!isStdAA(atgroup))
{atr = "" + linkedgroup + ""; modelSequenceNSR++;}
else atr = atgroup;
}
else if (match(ctype, "nucleic"))
{
if (!isStdNucleotide(atgroup))
{atr = "" + linkedgroup + ""; modelSequenceNSR++;}
else atr = atgroup;
}
else if (match(ctype, "carbohydrate"))
{
if (!isCHO(atgroup))
{atr = "" + linkedgroup + ""; modelSequenceNSR++;}
else atr = atgroup;
}
else if (atgroup == "DUM")
{
atr = 'DUM';
modelSequenceNSR++;
}
else
{
atr = "" + linkedgroup + ""; // ligands+, non-standard residues
modelSequenceNSR++;
}
// Link sequence numbers
atrlink = '.' +
seqnum + inscode + '';
//if (inscode) alert (atr);
//alert (atr);
//alert(atr + " " + seqnum);
if ((seqnum - 1) != sprev) // NOT one step up.
{
if (sprev != null)
{
// prefix |>|
if (seqnum < sprev) // not monotonic.
{
atr =
'' +
'|>|' + atr;
//alert(atr);
modelSequenceDecrements++;
}
// bold italic duplicate numbers
else if (sprev == seqnum) // duplicate numbers.
{
modelSequenceNumberDuplicates++;
// first char of atrlink is period.
atrlink = "." + atrlink.substring(1) + "";
}
// gap -N-
else // gap
{
gapsize = seqnum - sprev - 1;
atr =
'-' +
(seqnum - sprev - 1) +
'-' + atr + '';
modelSequenceGaps++;
if (gapsize <= modelSequenceGapCutoff && !gapgt8)
{
modelSequenceGapSum += gapsize;
}
else gapgt8 = true;
modelSequenceGapSumAll += gapsize;
//alert("gapgt8=" + gapgt8 + " gapcount=" + modelSequenceGaps + " gapsize=" + gapsize +
//" sum=" + modelSequenceGapSum + " sumAll=" + modelSequenceGapSumAll);
}
}
}
atr += atrlink;
//else if (ul) {atr = '' + atr; ul = false;}
// Color insertions magenta.
icaret = atr.indexOf("^");
if (icaret != -1) icaret = atr.indexOf("^", icaret + 1); // get second ^!
if (icaret != -1) //
{
atr = atr.substring(0, icaret) +
'' +
inscode + '' + atr.substring(icaret + 2) + '';
//alert(atr);
}
//if (!(seqnum%5)) // extra () are essential!
/*
if (!(is%5) && is) // extra () are essential!
{
atr += "
";
}
*/
//alert(atr);
seqarr[is] = atr;
sprev = seqnum;
}
atseq = seqarr.join(" ");
//alert(atseq.substring(atseq.length - 600));
return(atseq);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Populates choChainIDList. Like chainList, it is an array with identical chains
// grouped in a single element.
// THIS NOW DELETED FUNCTION INADVERTENTLY DUPLICATED
// groupBySeqID() in moltab.js which is called by
// getNoHeaderChains() in moltab.js.
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function charsToArray(charlist) // charlist is a string ABCDEF.
{
var newarr = [];
for (var iat=0; iat < charlist.length; iat++)
{
newarr.push(charlist.charAt(iat));
}
return newarr;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function offerFoundContacts()
{
doViewLink(contactsIndex);
setTimeout("offerFoundContacts2()", 500); // 50 failed, 100 worked.
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function offerFoundContacts2()
{
document.contactsForm.selectFound.checked = true;
changePreContactsView();
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function dotPDBExists(callback) // code from AI query.
{
const xhr = new XMLHttpRequest();
xhr.open('HEAD', pdbURL, true); // not 'GET'.
xhr.onreadystatechange = function()
{
if (xhr.readyState === 4)
{
if (xhr.status === 200) callback(true); // file exists.
else callback(false);
}
}
xhr.send();
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Check for existence of a .pdb file not in a URL.
// Could not get jmolEvaluate to work here for reasons unclear.
/*
var fileCheckError;
function pdbFileExists()
{
alert("hi");
if (!dotPDB || molIncludesHTTP) return true; // maybe, but not the job here.
var xcmd = '~filecheck = load("' + localPDBFiles + pdbFilename + '", TRUE);\n' +
'~filecheckerror = ~filecheck.find("error", "");\n' +
'javascript fileCheckError = jmolEvaluate("~filecheckerror");\n';
runBareSpt(xcmd);
alert(fileCheckError);
//var xex = jmolEvaluate("~filecheckerror");
//alert(xex + " typeof=" + typeof xex);
return false;
//if (xex ) return false;
return true;
}
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */